Scripto & Item Completion %

I was wondering whether there’s any built in way to access a “percent complete” statistic for an Omeka item’s Scripto transcriptions?

Ideally I’d like to store a complete = true|false on each page, and a percent complete = [pages.with.complete === true] / [item.numberOfPages] on each item.

I’ve written that functionality in my old Scripto project in JavaScript, but for my new project I’d like to stay closer to vanilla Omeka/Scripto. I’ve written a php function for the current project that gets a page’s complete = true|false from MediaWiki and iterates over all the pages in an item on the item’s page, but that’s a slow approach, and unnecessarily recalculates data at a point when it may not have changed.

So I was about to rebuild the Scripto “save” button to add a kind of calculator step – adding some kind of complete=true status to the page’s metadata, and, for the item metadata, it could merely “+1” to a %complete value (eg: a percent complete value could be stored, not as a percent, but as a number of completed pages; updating that value would be trivial (unlike having to re-check every page and divide), and the actual percentage could be calculated at the moment that the item’s “tile” is displayed.

But I thought I’d check with you folks before I did all that. It seems like the kind of functionality that most Scripto projects would like to have, so it occurred to me that maybe I’m reinventing the wheel.

If I understand you correctly, you could do this:

$mediaCount = $scriptoItem->mediaCount();
$approvedMediaCount = $scriptoItem->isApprovedMediaCount();
$percentApproved = round(($approvedMediaCount / $mediaCount) * 100);

Thanks for the quick reply! But–yeah–I think I didn’t clarify: I’m hoping to get the Scripto completion data into the Omeka interface, rather than Scripto’s. Is the $scriptoItem available in the Omeka pages?

(Due to a need for complex filtering and control of the item presentation, we’re trying to keep users in Omeka most of the time. The flow we’re working on is like this:

  1. Home (Omeka) →
  2. All Items/Search Results/Featured Items pages (Omeka) →
  3. Individual Item (Omeka) →
  4. Individual Image file (Scripto Edit Page) →
    • On Save: stay on edit page;
    • Next → Scripto edit page for next page
    • Prev → “”
    • Cancel/Back → Omeka Item page

That was another reason to rework the Save functionality, to prevent it from navigating, since we’re hoping to keep users on the Edit page after they’ve saved their work.)

You can get a $scriptoItem this way in a view template:

// Let's say the item ID is 123
$scriptoItem = $this->api()->searchOne('scripto_items', ['item_id' => 123]);

Thank you! It looks like that will work. I had to do:

$scriptoItem = $this->api()->searchOne('scripto_items', ['item_id' => 123])->getContent();

but after that, the mediaCount() and approvedMediaCount() calls returned values correctly.

The final code (with an if block that I probably don’t need anymore) was:

    $scriptoItem = $this->api()->searchOne('scripto_items', ['item_id' => $resource->id()])->getContent();

    if ($scriptoItem !== null && method_exists($scriptoItem, 'mediaCount') && method_exists($scriptoItem, 'isApprovedMediaCount')) {
      $mediaCount = $scriptoItem->mediaCount();
      $approvedMediaCount = $scriptoItem->isApprovedMediaCount();
      $percentApproved = round(($approvedMediaCount / $mediaCount) * 100);
    } else {
      $percentApproved = 0;
    }

and if anyone wants the progress bar itself, I just did it with a css gradient

            <div 
              class="progress-bar" 
              style="background-image: 
                linear-gradient(to right, 
                  #f0f 0%, 
                  #f0f <?= $percentApproved ?>%, 
                  #f0f0 <?= $percentApproved ?>%, 
                  #f0f0 100%
                );">
                  <?php echo $percentApproved ?>%
            </div>

(Magic pink is my testing color. Not recommended in production.)

Ok I’m more confused than I thought.

The item-level completion data has been achieved and there has been great rejoicing.

However, now I’m working on a page-level, binary, “done/not done” thing – and I’m not able to find the page data.

the item approach works just fine:

/* view/common/r-p-b-l/media-list.phtml */

$scriptoItem = $this->api()->searchOne('scripto_items', ['item_id' => $resource->id()])->getContent();

$resource->id();
// 11318
$scriptoItem->id();
// 11318
// hooray!

But when I apply that to pages of the item (aka media), it does some funky business:

/* view/common/r-p-b-l/media-list.phtml */

$scriptoMedia = $this->api()->searchOne('scripto_media', ['media_id' => $media->id()])->getContent();

$media->id();
// 11720
$scriptoMedia->id();
// 3233 ??

Am I getting the wrong “media” there?

Hi @nwhite ,

I don’t think I would assume you have the wrong media. It looks like you’re searching the scripto_media table for media based on the foreign key in media_id.

// Returns the primary key or `id` column  for the Media
$media->id(); 

// Returns the  primary key or `id` column for Scripto Media 
// Does not the foreign key stored in the media_id column, which would be the same as above
$scriptoMedia->id();

Ok I figured it out – and it was a typo. >:( At some point in all this,

searchOne('scripto_media', ['media_id' => $media->id()])
became
searchOne('scripto_media', ['media_id', $media->id()])

As a result, scripto_media wasn’t being filtered at all, so search() was actually returning ALL media, and then searchOne() just passed along the 0th one – so it didn’t matter what media I searched for, all media would’ve returned media_id=3233!

(Of course to figure that out, I had to trace searchOne() all the way back to Api/Manager.php, to understand how the search query was constructed; I went into the SQL and tried to find overlap between the returned data, like, maybe id=3233 had 11720 somewhere else in there… Quite an adventure.)

Thanks for your help!