how get First Access date on moodle? - moodle

i use a plugin : Course completion overview
but i need one more column with the date of first access of users,
what sql i need?

To add the user first access to Moodle, you have to edit the index.php file in the plugin folder and edit this MySQL request:
$compdatacoursesql = "SELECT u.id AS userid, c.id as courseid,
[...]
u.email,
[...]
by adding:
u.firstaccess AS userfirstaccess,
So it will be:
$compdatacoursesql = "SELECT u.id AS userid, c.id as courseid,
[...]
u.email,
u.firstaccess AS userfirstaccess,
[...]
Then in the foreach add
$a[] = ($row->userfirstaccess? date('jS F Y H:i:s', ($row->userfirstaccess)) : 'Never connected');
PHP date() function make the date human readable

Related

Knex, Objection.js, How to sort by number of associations

I have an Express API using Postgres via Knex and Objection.
I want to set up a Model method or scope that returns an array of Parent model instances in order of number of associated Children.
I have looked through the Knex an Objection docs
I have seen the following SQL query that will fit, but trying to figure out how to do this in Knex:
SELECT SUM(O.TotalPrice), C.FirstName, C.LastName
FROM [Order] O JOIN Customer C
ON O.CustomerId = C.Id
GROUP BY C.FirstName, C.LastName
ORDER BY SUM(O.TotalPrice) DESC
This is how it should be done with knex (https://runkit.com/embed/rj4e0eo1d27f):
function sum(colName) {
return knex.raw('SUM(??)', [colName]);
}
knex('Order as O')
.select(sum('O.TotalPrice'), 'C.FirstName', 'C.LastName')
.join('Customer C', 'O.CustomerId', 'C.Id')
.groupBy('C.FirstName', 'C.LastName')
.orderBy(sum('O.TotalPrice'), 'desc')
// Outputs:
// select SUM("O"."TotalPrice"), "C"."FirstName", "C"."LastName"
// from "Order" as "O"
// inner join "Customer C" on "O"."CustomerId" = "C"."Id"
// group by "C"."FirstName", "C"."LastName"
// order by SUM("O"."TotalPrice") desc
But if you are really using objection.js then you should setup models for your Order and Customer tables and do something like this:
await Order.query()
.select(sum('TotalPrice'), 'c.FirstName', 'c.LastName')
.joinRelated('customer as c')
.groupBy('c.FirstName', 'c.LastName')
.orderBy(sum('TotalPrice'), 'desc')
In case anyone is interested, I just included the raw SQL query as follows:
router.get('/leaderboard', async (req, res) => {
let results = await knex.raw('SELECT users_id, username, COUNT(acts.id) FROM acts INNER JOIN users ON acts.users_id = users.id GROUP BY users_id, username ORDER BY COUNT(acts.id) DESC');
console.log(results);
res.json(results.rows);
});

How to implement this relationship in Eloquent?

I have SQL this query:
SELECT f.follower_id, u.fname, u.lname
FROM followers f
INNER JOIN users u ON f.follower_id = u.id
WHERE f.user_id = $user_id
AND u.status = 1
ORDER BY fname, lname
LIMIT 10
I have two models: User and Follower.
A user can have many followers, and each follower has its own user data. I want to be able to get all of a user's followers (who have a status of 1) by doing something like this:
$followers = User::get_followers();
Add this to your User model:
public function followers()
{
return $this->belongsToMany(User::class, 'followers', 'user_id', 'follower_id')
->where('status', 1)
->orderBy('fname')
->orderBy('lname');
}
Then you can access them like this:
$followers = $user->followers;

LINQ Join to Top(1)

I am again struggling with syntax for a LINQ query.
I looked at LINQ Join on top 1 but I'm still pretty confused!
A User can have 0-n Photos.
I have the SQL I need but cannot work out how to implement this in LINQ. The SQL is this:
SELECT User.Name, Photo.ImageUri
FROM User
LEFT OUTER JOIN Photo
ON Photo.Id =
(
SELECT TOP 1 Id
FROM Photo
WHERE UserId = User.Id
)
I am using Entity Framework (.NET Core) and have tried the following:
var users = _context.Users.Select(x => new
{
name= x.Name,
photo = x.Photos.Select(c => c.ImageUri).FirstOrDefault()
});
The problem with this is that for every user it creates a SELECT on Photos. I was looking for a solution that did not require an additional query on the database for every user record.

JPQL SELECT ElementCollection

I have an entity "Post" with this property:
#ElementCollection
#CollectionTable(name ="tags")
private List<String> tags = new ArrayList<>();
Then i have a native select query with a group by. The problem is now how can i select the property tags?
My select query:
Query query = em.createQuery("SELECT p.id,MAX(p.createdAt),MAX(p.value) FROM Post p JOIN p.tags t WHERE t IN (?1,?2,?3) GROUP BY p.id ORDER BY COUNT(p) DESC");
/* ...
query.setFirstResult(startIndex);
query.setMaxResults(maxResults);
List<Object[]> results = query.getResultList();
List<Post> posts = new ArrayList<>();
for (Object[] result : results) {
Post newPost = new Post();
newPost.setId(((Number) result[0]).longValue());
newPost.setCreatedAt((Date) result[1]);
newPost.setValue((String) result[2]);
posts.add(newPost);
}
return posts;
how to select the property tags?
Don't know if it will help, but in JPA2.1 Spec, part 4.4.6 Collection Member Declarations, you can do that:
SELECT DISTINCT o
FROM Order o, IN(o.lineItems) l
WHERE l.product.productType = ‘office_supplies’
So I guess with your case, you could try:
SELECT p.id,MAX(p.createdAt),MAX(p.value), t
FROM Post p, IN(p.tags) t
WHERE t IN (?1,?2,?3)
GROUP BY p.id, t
ORDER BY COUNT(p) DESC
Note: I added t to the GROUP BY, since it would not work with the query without using an aggregate function.

LINQ query to fetch entities that include custom formatted field of associated entities?

A bit rusty on LINQ
I want to get a single result from related tables for a given user. See schema below.
Each user has one or more roles. I want a list of usernames and a custom string that is a list of their roles in a format such as "Role1 - Role2 - Role3", where the values are the RoleNames associated with the UserRole/Role for that user.
Role
=====
RoleId
RoleCode
RoleName
UserRole
========
UserRoleId
RoleId
UserId
Users
======
UserId
UserName
Testing it out in LINQpad, I can get a list of usernames and their roles, but instead of the RoleName, I want a single field in the result to be a formatted string of ALL the users roles, as mentioned above.
Here is what I have now. How can I construct a list of the roles for each user?
from u in Users
join ur in UserRoles on u.UserId equals ur.UserKey
join r in Roles on ur.RoleKey equals r.RoleId
select new {
u.UserId,
u.UserName,
r.RoleName
}
Add group by UserName to your LINQ query, and use string.Join to format the roles separated by "-".
You can test this in LINQPad -
var Roles = new [] {new{RoleId=1,RoleCode="SU",RoleName="Super User"},new{RoleId=2,RoleCode="PU",RoleName="Power User"}};
var Users = new [] {new{UserId=1,UserName="Bit Shift"},new{UserId=2,UserName="Edward"}};
var UserRoles = new [] {new{UserRoleId=1,RoleId=1,UserId=1},new{UserRoleId=2,RoleId=2,UserId=1},new{UserRoleId=3,RoleId=2,UserId=2}};
var userRoles = from u in Users
join ur in UserRoles on u.UserId equals ur.UserId
join r in Roles on ur.RoleId equals r.RoleId
select new
{
u.UserId,
u.UserName,
r.RoleName
}
into userRole
group userRole by userRole.UserName into userGroups
select new{ UserName=userGroups.Key, Roles = string.Join(" - ", userGroups.Select(ug => ug.RoleName))};
userRoles.Dump();
More succinct version, which includes UserId+UserName in result -
var userRoles = from u in Users
join ur in UserRoles on u.UserId equals ur.UserId
join r in Roles on ur.RoleId equals r.RoleId
group r by u into userGroups
select new{ User=userGroups.Key, Roles = string.Join(" - ", userGroups.Select(r => r.RoleName))};
userRoles.Dump();
When executing against SQL database, you need to the query split into two parts, as String.Join is not supported by LINQ to SQL, like this -
var userRoleGroups = (from u in Users
join ur in UserRoles on u.UserId equals ur.UserId
join r in Roles on ur.RoleId equals r.RoleId
group r by u into userGroups select userGroups)
.ToList(); // This causes SQL to be generated and executed
var userRoles = from userGroups in userRoleGroups select(new{ User=userGroups.Key, Roles = string.Join(" - ", userGroups.Select(r => r.RoleName))});
userRoles.Dump();
Or try using Aggregate instead of String.Join, as you suggested, like this -
var userRoles = from u in Users
join ur in UserRoles on u.UserId equals ur.UserId
join r in Roles on ur.RoleId equals r.RoleId
group r by u into userGroups
select(new{
User=userGroups.Key,
Roles = userGroups.Select(s => s.RoleName).Aggregate((current, next) => current + " - " + next)});
userRoles.Dump();
Group the RoleNames over the UserNames and use String.Join to get the desired result.
from u in Users
join ur in UserRoles on u.UserId equals ur.UserKey
join r in Roles on ur.RoleKey equals r.RoleId
group r.RoleName by u.UserName into grp
select new {
UserName = grp.Key,
Roles = String.Join(" - ", grp)
};
This will not return the UserIds. If they're important for the result, you need to change the code to
.... join as above
group r.RoleName by new {u.UserId, u.UserName} into grp
select new {
grp.Key.UserName,
grp.Key.UserId,
Roles = String.Join(" - ", grp)
};
This will create a grouping key containing both UserId and UserName, so you have that available in your select.