MarkUs Blog

MarkUs Developers Blog About Their Project

Status Report for Sept 30th

without comments

Nathan

Last Week:

  • address comments on rpsec pr and resubmitted pr
  • submitted a pr for issue #1701 (download all files button not working, include annotations not working for single file downloads)

This Week:

  • address any comments on two outstanding prs
  • start working on submission tagging project
  • code investigation, how to interact with database
  • what steps need to be taken to display table?
  • what steps need to be taken to insert tags and associate it with a submission?

Irene

This week:
* Created and sent out user study questionnaire regarding MarkUs dashboard
* Submitted pull request for Group model RSpec tests
Next week:
* Fix Issue #1720 “Ascending/Descending icons change column widths”
* Gather responses to questionnaire and start designing wireframes (Issue #1668 Revamp Admin Dashboard)

 

Tori
Last week:
* learning more ruby/rspec
* testing issue #1722
* addressing Hound comments on issue #1722 and AssignmentFile spec
This week:
* finish testing issue #1722 (i.e. issue #1799)
* get a feel for React tables
* begin conversion of table in Repository view to React
Darren
Last week:
* started getting a feel for the RJS files in app/views understanding what I have to change.
* Understanding how to transition the code in app/views from prototype to jquery.
This week:
* continue to learn what exactly needs to be changed in the RJS files
* and implement the changes from prototype to jquery
Brian
This week:
  • Submitted a fix for issue #1726 (https://github.com/MarkUsProject/Markus/issues/1726). This issue was a stylistic problem where upload buttons would remain enabled even when a file was not selected. Now, a jQuery script was written that only enables the button when a file is selected. This change was implemented across multiple modal dialogs.
  • Started working on the back-end for the new assignment tagging system by creating model and controller Ruby scripts for the tags. With this, a schema was created to illustrate what data needed to be stored and the relationships between the Tag model and other models.
Roadblocks:
  • A major roadblock was understanding how Ruby on Rails stores data using ActiveRecord. This roadblock is still ongoing but the issue is starting to make more sense.
Next week:
  • Getting a simple working model and controller implemented for the Tag feature. Although it won’t have full functionality, it will serve as a starting point for implementing the tagging feature.

Written by Nathan Chow

September 30th, 2014 at 12:46 pm

Posted in Status Reports

Status Report for Sept 26th

without comments

Chris

Last Week:

  • Attended the Code Sprint in Toronto!
  • Submitted pull request for rspec tests.
  • Worked on the CSV large file upload issue (#1766).
  • Started researching pdf annotation improvements.

This Week:

  • We were going to try and setup a VM similar to the production server so that the CSV issue and be replicated and logged. When that is complete I will continue trying to resolve this issue.
  • Write a blog post regarding the state of web annotations on PDF’s.
  • Create prototypes of the PDF annotation feature with PDF.js to determine the feasibility of a new annotation system.

Jakub

Last Week:

  • Attended Code Sprint
  • Fixed issue #1768 (API for adding users doesn’t work for REMOTE_USER_AUTH)
  • Fixed issue #1718 (Submissions table missing asset)

This Week:

  • In the process of becoming an rspec master.
  • Started translating the assignment rake test to an rspec test.
  • Also thoroughly going though the assignment model logic itself; added association options and attribute contraints

Yusi

Last Week:

  • Attended Code Sprint
  • Fixed issue #1714 (“Reset API Key” button causes help text to show )
  • Fixed issue #1655 (Adding two flexible criteria *WITH SAME NAME* silently fails)
  • Started Mark model rspec test

This Week:

  • Continue working on Mark model rspec test
  • Make a plan for what need to be changed for the upgrade

Written by Chris Kellendonk

September 26th, 2014 at 11:27 am

Status Report for Sept 23rd

with 2 comments

Irene

Last week:

  • Attended the code sprint in Toronto!
  • Fixed Issue #1959 Spreadsheets “Select: All” button doesn’t select all
  • Changed spreadsheets to bigger page sizes

This week:

  • Continue working on Issue #1920
  • Expand on rspec for group model
  • Write questionnaire for Dashboard usability study

 

Bryan

Last week:

  • Attended the code sprint in Toronto and met the rest of the UCOSP participants!
  • Fixed Issues #1728 (Can’t upload hidden assignments) and #1729 (Spreadsheet csv shouldn’t require “”).
  • Created an rspec test for the Annotations model.

This week:

  • Continue working on Issue #1726 with Nathan.
  • Begin to explore a feature to allow TAs and admins to tag assignments. Currently exploring different options on how this can be carried out. See feature #8 for more information.

Darren

Last week:

  • Attended the code sprint in toronto
  • worked on my assigned rspec tests
  • got my development environment set up

This week:

  • Finish off issue #1763
  • start transitioning from prototype to jquery which is issue #1496

Nathan

  • Attended code sprint in toronto
  • finished off first issue
  • started working on issue 1726 and 1701
  • created rspec test for image_annotations

This week:

  • address pull request comments on rspec tests
  • continue working on issue 1701

 

Written by Irene Fung

September 22nd, 2014 at 11:02 pm

Posted in Status Reports

Fall 2014 Code Sprint :: MarkUs Team

without comments

MarkUs

Team dinner at WVRST sausage hall

 

Written by Irene Fung

September 20th, 2014 at 9:14 am

Posted in Meetings

Status Report for Sept 15th

without comments

Bryan
This week:
  • The development environment for the MarkUs Project was setup using Vagrant and Ubuntu 13.
  • Submitted pull request #1750 (https://github.com/MarkUsProject/Markus/pull/1750) for a fix to issue #1728 (https://github.com/MarkUsProject/Markus/issues/1728). This issue was a bug that improperly formatted hyperlinks on the assignment dropdown for assignments marked as hidden. To test the fix, several new assignments were created and marked as hidden. The assignment sidebar worked properly for all these new assignments.
Roadblocks:
  • There was some trouble setting up Vagrant and MarkUs on Windows.  To get around this, Ubuntu was installed and MarkUs was set up on this environment instead.
Next week:
  • Getting ready for the UCOSP code-sprint in Toronto! Working on new issues and fixing bugs.
Chris
This Week:
  • Setup a stable and usable virtual machine running to develop the application in. I had Vagrant running almost immediately and MarkUs shortly there after. However I then noticed that while the application was running in Vagrant there were very long page load times (20+ Seconds). Instead I imported the the VirtualBox machine into Parallels. That improved page load times to about a 1 – 5 seconds on average.
  • I fixed Issue #1493 and submitted a pull request. This was a problem with how totals were displayed and updated in the marks spreadsheet.
  • I noticed the “total_grade” column is managed in ruby and am unsure why this value is being calculated server side and having to be managed manually when it could simply be a calculated SQL column. I have filed an issue (#1762) in regards to this.

Next Week:

  • If Issue #1762 is approved I would like to fix that issue.
  • Complete some more tasks/issues. Possibly some of the spreadsheet related items since I now have some experience working with that component in the application.
  • Attend the Code Sprint!
Irene
This week:
  • Set up development environment. There was trouble setting up MarkUs on Windows 8.1 so Ubuntu 14.04 was installed and it is now working fine.
  • Familiarizing myself with Ruby and Git.
  • Work on Issue #1759.
Next week:
  • Fix Issue #1759, test and submit pull request.
  • Head to Code Sprint in Toronto!
Jakub
This week:
  • Fixed Issues #1746 & #1748. Pull requests have been accepted.
Next week:
  • Determine why some rake tests appear as failing for my environment. Potentially create a couple of issues — depending on their validity — and pick up more issues to tackle.

Nathan
This week:

  • Set up dev environment.  Have a fix for issue 1730
Before code sprint:
  • submit pull request for my fix

Yusi
This week:

  • Set up development environment.
  • Read development guideline and other documentation of Markus
  • Submit pull request of issue-1735
Next week:
  • More reading and prepare for the Sprint.
  • Wait for feedback of the pull request.

Tori
This week:

  • Setting up the development environment. Had some issues dual booting Linux Mint 17 and Windows 8.1 and had to format/repartition my poor laptop 4-5 times (all is well now).
  • Reading up on Git and Ruby tutorials
  • Reading developer guidelines for MarkUs

Next week:

  • Continue getting familiar with Ruby, Rails and MarkUs
  • Get assigned an issue to work on
  • Attend Code Sprint!

Alex & Mark
This week:

  • Solve issue #1456 which is related to SVN repo creation issues (which is a good primer to our main project listed for next week)
Next week:
  • Continue our investigation into how Git has been integrated so far into Markus on the Git branch and assess the contributed code for quality before adding more.

Written by Irene Fung

September 15th, 2014 at 11:22 pm

Fall 2014 Project List

without comments

There was a lot of work done on MarkUs this summer. (Thanks David, Lawrence, Eugene, Ealona, Su, Mark, and Angelo!)  The strange thing about getting lots done is that it is more obvious what needs to be done next, so I have a long list of potential projects for the fall.

1. React tables for spreadsheets, repo browser, and refactoring #1696 (large)
This summer Lawrence made some major changes to how tables are implemented in MarkUs. The biggest change was to use the React JS library to implement the tables. This got rid of a lot of Prototype code, and made it possible to do better sorting and filtering on the tables. Two tables remain to be converted: the repo browser and the spreadsheet. There is also some refactoring work to do, now that we know better how React works. Also, there is some remaining styling work for the new tables.

2. Get rid of the rest of Prototype #1496 (small)
We have slowly been removing all the Prototype code, and replacing it with JQuery. Aside from the tables that still need to be converted to use React, there are still a few places where Prototype has not been fully replaced. This would be a good project for a student with an interest in learning (more) Javascript/JQuery.

3. Git backend #1698 (large)
MarkUs stores student submitted files in subversion. Instructors have the option allowing students to submit files through the web interface, or may disable the web interface and require that students use subversion directly to submit their work. One repo is created per student or per group, if groups are allowed. Each assignment is a directory in a repo, and MarkUs tries to reuse repos where possible.
We are in the process of adding support to store student submissions in git repos, while at the same time maintaining support for subversion. Because git and subversion have quite different models, this task is more involved that it first appears. Much work has been done on this in the past; new students will spend some time reviewing the existing progress for this feature.

4. Refactor the Admin Dashboard #1668
Reloading the graphs takes too long and isn’t really necessary (medium)
The Dashboard view has graphs that show the mark distribution for each assignment. They take some time to load and aren’t really that useful to the administrators. It is nice to get some summary information, but we need to rethink what the best summary data is to display and how to do it. This will be partially a UI design project since we need to rethink the purpose of the dashboard view and what information is most useful to display.

5. Rails 4.0 upgrade (medium?)
Mark Rada began the work of upgrading from Rails 3 to Rails 4 this summer. He did some great work on strong parameters, but there are other parts of the upgrade process that still need work. This project would be a great one for students with some understanding of Rails, and have an interest in how the Rails framework works.

6. PDFs – is there a better library to use? (research: small; implementation: large)
Students may submit PDF files. MarkUs converts theses files to jpegs (using Ghostscript) so that the images are a fixed size to facilitate annotation. It is time to do some research to see if better options are available. The goal is to maintain support for annotating PDF files without needing to convert the files. Actually switching to a new PDF option will probably require multiple terms/students.

7. Rspec tests (large, but an ongoing effort)
This summer saw a major effort to change to Rspec tests. Ealona and Su wrote a guide to writing Rspec tests, and have done a fair bit of work implementing some Rspec tests. A goal of this term is to have everyone on the team write some Rspec tests. It will be a good way to really learn what the models and controllers are supposed to do, and will move the project forward. We plan to set aside a few hours at the sprint for writing tests. There are some outstanding Github issues related to missing tests that can easily be closed with some work.

8. Tagging student submissions #886 #325 (medium?)
This is a feature request that we hope will satisfy several use cases. Instructors and TAs have asked for a way to flag assignments to bring them to someone’s attention. TAs have also asked for a way to categorize the submissions so that they can do a quick first pass over them. The ability for adding general tags to student submissions will hopefully solve a number of these kinds of issues.

9. Summary page of all the marks for all students (smallish)
There is currently no view that combines marks from all of the different assignments and spreadsheets. This table would look a lot like the Submissions table or the Summaries table, but would have one column for each assignment and spreadsheet. A new feature would be a way for the administrator to specify a weighting for each piece of work to produce a total. (Good for someone not familiar with Rails.)

10 MathJax support for annotations? #285 (medium?)
It would be nice to be able to use math symbols in annotations. The MathJax library seems to be what we want, and some work has been done on this.

11. UX Refresh of the submissions table (Includes #75) (medium)
We haven’t taken a serious look at what is in the submission table for a long time. For example, we probably don’t need the “can begin grading” field. We would also like to be able to show the grader(s) for each group.

12. Section due dates don’t work #1676 (small)
Some courses would like to have a different due date for each section. This feature seems to have numerous problems with it. There is also a proposal to change the UI for how sections are added.

Written by Markus

September 2nd, 2014 at 6:50 pm

Say Goodbye to the n+1 Query Problem

without comments

With the declarative power of Rails’ ActiveRecord, it’s very easy to write code that are prone to performance bottleneck, such as an issue commonly known as the n+1 query problem.

Consider the following simplified MarkUs models:

class Grouping < ActiveRecord::Base
  has_many :ta_memberships
  has_many :tas, through: :ta_memberships
end

class TaMembership < Membership
  belongs_to :ta
  belongs_to :grouping
end

class Ta < User
  has_many :ta_memberships
  has_many :groupings, through: :ta_memberships
end

TaMembership is a join model between Ta and Grouping that represents the assignment of a TA to a grouping so that the TA has the permission to mark students in the grouping.

A view may have a table of groupings and need to display all the TAs assigned to each grouping (in ERB), as in this (overly-simplified) example:

<% Grouping.find_each do |grouping| %>
  <span class='ta'><%= grouping.tas %></span>
<% end %>

This all looks good until we inspect the query log and find that such a simply snippet of code generated a lot of SQL queries:

SELECT * FROM groupings

SELECT * FROM tas JOIN ta_memberships ON ta_memberships.ta_id = tas.id WHERE ta_memberships.grouping_id = 1

SELECT * FROM tas JOIN ta_memberships ON ta_memberships.ta_id = tas.id WHERE ta_memberships.grouping_id = 2

SELECT * FROM tas JOIN ta_memberships ON ta_memberships.ta_id = tas.id WHERE ta_memberships.grouping_id = 3

SELECT * FROM tas JOIN ta_memberships ON ta_memberships.ta_id = tas.id WHERE ta_memberships.grouping_id = 4

...

SELECT * FROM tas JOIN ta_memberships ON ta_memberships.ta_id = tas.id WHERE ta_memberships.grouping_id = 42

This is because ActiveRecord lazily loads associations by default, i.e., the association model is only loaded (through a SQL query to the database) when the attribute is accessed the first time. In the above example, an initial query is issued to get a list of Grouping models. Then, for each Grouping instance, the tas attribute is accessed, generating another SQL query to get the associated model instance. This results in a total of n+1 queries, where n is the number of groupings, hence the name the problem. In a networked production environment, the round-trip cost of issuing a database query is a significant overhead due to network delays. Therefore, in general, having n queries perform poorly compared to having only one (or some constant number of) queries that achieve the same.

In this case, the performance can be improved by avoiding the n+1 queries and use only a few. In Rails, this can be achieved using eager loading of associations.

<% Grouping.includes(:tas).find_each do |grouping| %>
  <span class='ta'><%= grouping.tas %></span>
<% end %>

By using includes, Rails takes care of the eager loading of the tas association and issues only two queries:

SELECT * FROM groupings

SELECT * FROM tas JOIN ta_memberships ON ta_memberships.ta_id = tas.id WHERE ta_memberships.grouping_id in (1, 2, 3, 4, ..., 42)

Just like the where and joins method, The includes method can handle nested associations as well:

Grouping.includes(ta_memberships: [{ ta: :groupings }, :grouping])

Note that while the nested association can be loaded, sometimes it’s redundant and causes too much overhead when the nesting level gets too deep. In the above snippet, grouping.ta_memberships is a collection of TaMembership instances, where each instance has a ta association, and each one has a collection of Grouping instances. All these eagerly-loaded instances already form a pretty large and complex structure — Large structure causes memory bottlenecks. Think about whether there is any redundancy in the eagerly loaded model instances and whether you can re-organize the view or controller to simplify the structure.

Normally the use cases of includes are in the controllers, in which the model instances are eagerly loaded and passed to the view (the above examples where the includes calls are in the view are only for demonstration purposes). However, when multiple controllers are using the same set of eagerly loaded associations, consider writing a scope for the model (in Rails 4):

class Grouping
  scope :with_tas, -> { includes(:tas) }
end

Or the Rails 3 way:

class Grouping
  def self.with_tas
    Grouping.includes(:tas)
  end
end

MarkUs still has quite a few instances of the n+1 query problem at the time of writing. With the help of bullet, we can track the the remaining evil n+1 queries in the system.

Bulk operation — “n query problem”

The n+1 query problem occurs most likely when reading the database (i.e., doing SQL SELECT statements). A related problem, I call it “the n query problem” may occur when doing bulk operations such as INSERT, UPDATE and DELETE statements.

For example, the problem occurs when doing something like the following:

groupings.each do |grouping|
  grouping.some_attribute = foo
  grouping.tas.clear
  grouping.save!
end

This generates n UPDATE statements and n INSERT statements. These can normally be reduced to just one single query.

Bulk creation

Use activerecord-import. This is by far the most DBMS-independent gem for ActiveRecord bulk creation. ActiveRecord’s create method actually supports bulk creation, but it depends on the underlying database driver for Ruby to do the actual bulk creation. At the time of writing, the PostgreSQL driver pg still doesn’t support bulk creation.

For a sample usage, refer to Grouping.assign_tas.

Bulk update and bulk deletion

Use update_all and delete_all.

For a sample usage, refer to Grouping.unassign_tas.

Written by Su Zhang

August 20th, 2014 at 5:58 pm

Rails 4 Upgrade Notes

without comments

The Upgrading to Rails 4 guide is a good first step to understanding what needs to be changed. Rails’ own Upgrading Guide also has a list of things to pay attention to. Neither guide is a superset of the other, and I have had to look elsewhere to solve a few of the upgrading issues.

Here is a short list of the upgrading tasks that I could not back port which caused varying amounts of trouble:

 


 

Gemfile

You will want to look more closely at the versions that the first guide suggests. In most cases a newer maintenance release of a gem are available and should be selected instead.

One exception to this is the sass-rails gem. It will need to be set to version 4.0.1. Why? Because MarkUs needs a newer version of Sass. Sass 3.3.x is required because Sass 3.2.x fails to parse MarkUs scss code. Though it does not appear to make sense, the older sass-rails gem had a looser version specification for sass, and so by rolling back sass-rails to 4.0.1 we allow bundler to choose a newer version of sass itself.

When removing strong_parameters from the Gemfile, do not add the protected_attributes gem for backwards compatibility. MarkUs has already been upgraded to use strong parameters. Since strong_parameters is part of Rails 4, it should not be listed in the Gemfile.

Lastly, the minitest gem should be removed. Ralis 4 depends on minitest itself; it is best to avoid conflicting version requirements.

 


 

Routes

The catch-all route should get changed to work via :all HTTP verbs if possible:

https://github.com/MarkUsProject/Markus/blob/d0d76012350901cf4059d012fc4da575881ca876/config/routes.rb#L338

 


 

Configuration

In config/application.rb:20 “config.action_view.javascript_expansions” should be deleted. And then you will need to update the the “javascript_include_tag :defaults” line in  app/views/layouts/no_menu_header.html.erb to use the ‘application’ (with quotes) instead of :defaults.

Written by Mark Rada

August 14th, 2014 at 6:58 am

Using Gitolite

with one comment

We are in the process of implementing a git backend for MarkUs, and authentication/authorization on the repos is a big question.

It seems obvious that gitolite is the right choice for authorization, and because I’m planning to use it for courses as well, I thought I would write down what I have learned.  I still have to try it out, so I may have some of the details wrong.

The repos will be managed under a separate userid that I’ll call markus-git.  I suspect it should be a separate userid than the one that the application runs under, for safety. Only the markus-git user has file permissions on the repos, and users do not need any shell access.

I should also point out that if students are only accessing MarkUs through the web interface, then none of the rest of this is relevant to them.

Users will have to register a public key with gitolite, and we’ll want to automate this process so that it doesn’t require human intervention.  Then they can make a request like the following.

git clone markus-git@markus.cdf.toronto.edu:c2reidka.git

How does it work?

Gitolite sets things up so that when the ssh command is run, it first forces an authorization check. It uses the public key to create a message that only the private key on the client can interpret. If the client sends back the correct response, then the user will be granted access to the appropriate repos.

The gitolite-admin repo is cloned by the administrators (and will include the markus process). It contains the public keys as well as a configuration file conf/gitolite.conf that contains all of the access control information for the repos.

The nice thing about using the ssh approach is that there is no additional server to worry about.

One of the headaches we will have to resolve is how to handle students who have problems with their key pairs, and need to update their keys.

Written by Karen Reid

August 1st, 2014 at 12:42 pm

Carpentry in React

without comments

React is a JavaScript library for building user interfaces. It’s used heavily in production in both Facebook and Instagram (Facebook created React), and in my opinion is an awesome tool for building front-end interfaces.

The work I’m currently doing on MarkUs involves re-implementing, using React, the sortable, searchable, and filterable tables used for listing users and managing groups, graders, and submissions. This is a prime target because 1) the previous tables used a library that depended on Prototype, which we are trying to move away from, 2) these tables can be really slow, since the html for each row is rendered in the backend individually, and 3) the previous tables became messy enough (_boot.js.erb files and construct_xxx_table_row methods) to warrant a rewrite. Also because 4) React is awesome. So let’s go into how React works.

React, at its core, is a library for creating components: functional, reusable, and composable web views. It also manages to let you do this in an extremely simple fashion.

Each component has two objects: this.props and this.state. Props are generally for holding immutable data, while state holds mutable data. In a sortable table, for example, the titles of the columns would go into props and the currently sorted column and sort direction would go into the state. A component then defines a render method that is functionally pure; that is, given the same props and state, the output will always be the same. The render method also cannot cause any side effects.

Now, when React detects a state change of any kind, it’ll quickly re-render diffs for the new DOM based on the render method. Managing state suddenly got a lot easier.

Here’s an example from MarkUs: StudentsTable, which is the React component that defines the listing of students in the Users view. StudentsTable has two pieces of state: students, which is the array of student data that will be used to fill the table, and selected_students, which is an array of student ids that represent the selected students (duh) for doing actions on.

The render method then defines some column and filter objects (based on an ad hoc protocol; column objects have boolean attributes such as sortable and searchable while filter objects define a test for each object). Then it returns a div (really a React DOM object: React uses a virtual DOM) containing two other components: an ActionBox and a Table.

Here’s some demo code for StudentsTable:

StudentsTable = React.createClass({
  getInitialState: function() {
  // Sets this.state.students to
  // what the backend returns
  jQuery.ajax({
    method: 'GET',
    url: ...
    dataType: ...
    success: ...
    });
  },
  render: function() {
    // this stuff could and maybe should actually be moved to props
    var columns = [
      { id: "user_name", sortable: true, searchable: true },
      { id: "first_name" , sortable: true, searchable: true }
      ...
    ];
    var filters = [
      { name: 'all', func: function(student) { return student; } },
      { name: 'active', func: function(student) { return student.active; } },
      ...
    ];
    return (
      <div>
        <StudentsActionBox selected_students={this.state.selected_students}
                           onChange={this.refresh} />
        <Table data={this.state.students} columns={columns} filters={filters} />
      </div>
    }
});

Now, StudentsTable will pass down selected_students to ActionBox, which will receive it as props. This may be a little confusing since selected_students was in state originally, but it doesn’t matter. ActionBox doesn’t know, and ActionBox doesn’t care. Its render function should account for both state and props, and in fact it helps to think of selected_students as immutable, since what ActionBox really needs to be worrying about are things like the dropdowns and the grace credits input and managing that stuff.

We can see that the data flow is kept one-way: StudentsTable, as the parent of the ActionBox, doesn’t know about the state of the ActionBox, nor does it care. If you really needed to know something, like when StudentsTable should request new data, you can create a hook by passing down the refresh method as a prop and have ActionBox call it whenever it needs to. But in general, it’s best to keep upward data flow as minimal as possible.

The base Table is its own component with its own subcomponents like TableFilter, TableSearch, and TableHeader which may or may not have their own subcomponents. But these parts all work in the same way; they’re just smaller parts. This way, React makes reasoning about a specific level in the view hierarchy a lot simpler since everything is compartmentalized and data flows only one way. Consider implementing the same thing in jQuery or Prototype: it would probably require a crazy amount of reasoning about state (of which the size is huge — blame combinatorial explosion) and DOM manipulation.

In conclusion: React is dope. Its power comes from its means of combination and abstraction (sup Sussman). Check it out here.

Written by Lawrence Wu

June 16th, 2014 at 10:25 am