▣🔗 Asset Link - Use-case Drive

Not trying to ignore you here @Farmer-Ed… just lots of great input all around!

This is one I’m going to be implementing very soon for our spring rabbit breeding/management…

Wow, so many cans of worms here! :nerd_face: :sweat_smile: I don’t have much to say on these right now, but I’d be interested to hear more about what the goals/data look like for each of those scenarios/interfaces.

2 Likes

I updated this to include the date field and fix a bug where the scores were being recorded as “count” rather than “value” measures.

Holy cow I missed this! That’s awesome @Symbioquine! A perfect example of using a convention specification to develop a tool that creates comparable data! :smile:

Sheesh we need the ability to tag the “intended convention” of records like I described in https://www.drupal.org/project/farm/issues/3336224 - hopefully we can decide on a direction for that soon, so that tools like this can tag the convention when they create records!

3 Likes

This thread was bound to open a few cans. :smiley:

Happy to discuss anytime, probably need to sit down and iron out the exact goals of some of these myself, but I think they would be standard enough reports for livestock/ pasture management.

I’ll have to have a look at the work already done on pasture scoring too.

Good questions, @Symbioquine :

I ultimately want the text, so if the audio can be transcribed immediately to text (i.e. the way i input most texts of any significant length on my iPhone), that would be preferable.

Depending on what info/ used by whom, it is eitherTrello (kanban) boards, Google docs & sheets, or Notion (the only proper db in the lot) in a few edge cases. So we can say: mass-market Cloud SAS apps, as a rule -all of which have decent API access, as it happens.

As concerns provision of links to related log files, i like the direction this is going w/ @pat … And as to context-sensitive classification of assets, by QR code and/or GeoLocation, i’m thrilled to hear that you’ve got such capability integrated already.

Can’t wait to have this running on my iPhone &sync’d to my farmOS instance at Farmier!

1 Like

Sure

Of course there will be some challenges in grouping the logs in appropriate groups.
Ideally there would be groups for soil disturbance, drilling, weeding etc

In this treeview I tried to add some icons. [1] is an icon.
I put up this in the file browser. The treeview shows data relevant to the current field:

Let’s say I would compare the covercrops for each season. A bit tedious to expand Drilling group on all sesasons.
If I’m already looking at the current drilling group, I could tap on icon [4] and expand drilling group on all seasons.

Icon [1] could expand the current season.

Yea… Thats a problem.
I think of one season as the year the crop is harvested. And all logs referencing the plant asset is relevant.

But if a crop is drilled in autumn and harvested next autumn it gets more complicated.
And some may perhaps have multiple seasons pr year.

To keep it “simple”, Show all unarchived plant assets for the current field as the 1st tree-level.
And inside that, show all relevant log categories.

To complicate it, categories could be implemented.
For example a Crop category, with subcategories Soil disturbance, Weeding, etc…
The user would need to setup what category to use for this, and add appropriate category for each log.
It would give greate flexibility.

bilde bilde

quote=“Symbioquine, post:20, topic:1516”]
As you say, the seasons list could be quite long - perhaps there’d be an argument for allowing seasons to be archived or some other mechanism to only show the most relevant ones
[/quote]
Archiving could be a good solution. It gives the user control.

Hope this clarified rather than bringing the confusion to a higer level :innocent:

Another use-case/plugin idea: the ability to “clock-in” and “clock-out” on individual logs, which saves data to a (forthcoming) time quantity along with the ID of the user.

See Tracking work hours - #6 by mstenta

1 Like

DieselTracking.alink.vue

/alink/diesel-tracking

3 Likes

I have started to create a module for creating harvest logs with how much you harvest. FarmOS-Asset-link-plugins/Plants at main · jorblad/FarmOS-Asset-link-plugins (github.com) Right now I have hardcoded it to work with count and grams but my plan is try to get it to get the measures from farmOS and populating that in the choice list.

Edit: For now I don’t even know if it is possible to get them but I suppose it should be somehow.

1 Like

That’s awesome @jorblad! It looks like it’s already mostly working too :grin:

I’ve created a new topic where we can discuss it in more detail: ▣🔗 Asset Link - Harvest Plugin Fun!

2 Likes

I also created a plugin for filling up seeds with a purchase log so it needs the farm_ledger module to work. FarmOS-Asset-link-plugins/Seeds/AddNewSeedsToInventory.alink.vue at main · jorblad/FarmOS-Asset-link-plugins (github.com) My thought on the next one is to try to recreate the planting quickform where you also can subtract seeds from inventory but might start with a smaller to just use seeds. For the planting I need to be able to create new assets which I dont know if its that easy to do in asset link… and it also feels like a bit more of a project with much information to take get and data from many places that should work together. :sweat_smile:

2 Likes

It’s just the same as creating logs. i.e. Instead of log--observation the type might be asset--plant. Obviously, the attributes/relationships are different, but there shouldn’t be anything especially hard/surprising about it.

If it starts feeling overwhelming, consider reducing the scope… For example, ask yourself; “What is the minimum functionality I need to move my own use-case forward?” That might mean hard-coding some things or reducing generality/flexibility, but if it means you get a return on your time/coding investment sooner, it’s probably worth it.

2 Likes

Great, then I have a project for next week. I will probably see what I can reduce to get something useful that I can add on to later :slight_smile: Otherwise it might feel a bit overwhelming to take it all in one step :sweat_smile:

3 Likes

First version ready, it only creates the planting log and you choose both what seeds should be used and what kind of plant it is you are planting and there are probably lots of things that could be done in a better or more effective way but it feels like a good start :slight_smile:
FarmOS-Asset-link-plugins/Plants/PlantPlantsFromSeed.alink.vue at main · jorblad/FarmOS-Asset-link-plugins (github.com)

Edit: I have also added that it prefills species based on the asset and you can create new season, seed asset and/or species on the fly.
Edit 2: Also added possibility to upload a picture to the asset and fixed so that it won’t let you submit unless all mandatory fields are have something in them.

3 Likes

This is great, I’m glad to see some more complex use-cases coming together!

Just a few bits of feedback.

fetchAssetLink

I’m curious where this pattern came from? I don’t think you should ever need to do this…

// Create a function to fetch assetLink and store it in the ref
const fetchAssetLink = async () => {
  assetLink.value = await getAssetLink(); // Replace getAssetLink with your code to retrieve assetLink
};

// Fetch the assetLink on component mount
onMounted(fetchAssetLink);

The recommended ways to get an AssetLink instance are:

In a setup method/block;

const assetLink = inject('assetLink');

Otherwise, assetLink should be already available - such as in a plugin’s onLoad method.

findUnitTerm

I don’t think you should need to do this “double-lookup-else-create” pattern;

    const findUnitTerm = async entitySource => {
      const results = await entitySource.query(q => q
          .findRecords('taxonomy_term--unit')
          .filter({ attribute: 'name', op: 'equal', value: UNIT_NAME }));
      return results.flatMap(l => l).find(a => a);
    };

    let seedUnitTerm = await findUnitTerm(assetLink.entitySource.cache);

    if (!seedUnitTerm) {
      seedUnitTerm = await findUnitTerm(assetLink.entitySource);
    }

    if (!seedUnitTerm) {
      const unitTermToCreate = {
          type: 'taxonomy_term--unit',
          id: uuidv4(),
          attributes: {
            name: UNIT_NAME,
          },
      };

      seedUnitTerm = await assetLink.entitySource.update(
          (t) => t.addRecord(unitTermToCreate),
          {label: `Add '${UNIT_NAME}' unit`});
    }

See my feedback on the other thread about the " $relateByName directive". (Actually, it looks like maybe you’re already doing that and just need to delete the above code?)

Retrieving Entity Lists

Looking at the code like this;

  const results = await entitySource.query((q) =>
    q.findRecords('taxonomy_term--plant_type')
  );

  const plant_types = results.flatMap((l) => l);

I’m skeptical that this has been tested… I didn’t try running it, but I’m pretty sure that if you define a single query (instead of a list of them) in the entitySource.query lambda, then it will return a single list of entity results, not a list of lists. Thus the flatMap wouldn’t be necessary and would probably fail.

It’s also worth noting that entitySource.query doesn’t handle pagination automagically, so it could be surprising to users of your plugin to find that only some of the plant types show up (or are filterable) via the drop-down.

It might be better to use the EntitySelect component that Asset Link already provides as part of its plugin API: Asset Link docs EntitySelect (If we examine its code, we find that it doesn’t do pagination since showing a huge list isn’t very helpful, but instead it searches the entity source again if the user types in filter criteria.)

Getting Seeds by Name

It’s probably obvious, but code like the following would only work if your use-case has a very strong convention of seeds having unique names.

const seed = await assetLink.entitySource.query((q) =>
  q.findRecords('asset--seed').filter({ attribute: 'name', op: 'equal', value: seedAsset })
);
console.log('Seed object', seed)

It might also be better to pass the whole seed object through and use the object that your form already had access to instead of looking it up again.

Namespacing

In your other plugin here, I notice you’ve used the id net.symbioquine.farmos_asset_link.actions.v0.harvestPlant.

While it doesn’t hurt anything to put any text you want into that id, it is preferable to base the id on a domain that you control. This is a pattern that I established so that in the future Asset Link can be smarter about showing where functionality comes from and allowing plugins to be gracefully upgraded.

1 Like