Vapor 3 rendering complex call in Leaf - vapor

Here's the call that works with my API, but I need to render the result in leaf. This currently works fine with the API. Just not sure how where I'm grabbing the teams.
func getTeamsWithUsersForEvent(_ req: Request) throws -> Future<[TeamWithUsers]> {
let currentID = try req.parameters.next(Int.self)
print("currentID \(currentID)")
return Team.query(on: req).filter(\Team.eventID == currentID).sort(\Team.rawScore, .descending).range(..<5).all().flatMap(to: [TeamWithUsers].self) { team in
try team.map { team in
try team.users.query(on: req).all().map { users in
var score: Int
if users.isEmpty {
score = 0
}else{
score = team.rawScore / users.count
}
return TeamWithUsers(
id: team.id,
name: team.name,
icon: team.icon,
eventID: team.eventID,
rawScore: team.rawScore,
//users: users,
count: users.count,
teamScore: score
// teamScore: team.rawScore / users.count
)
}
}.flatten(on: req)
}
}
struct TeamWithUsers: Content {
let id: Int?
let name: String
let icon: String
let eventID: Event.ID
let rawScore: Int
//let users: [User]
let count: Int?
let teamScore: Int
}
Here's the what I've started. This is my context.
struct AllTeamsContext: Encodable {
let title: String
let teams: [TeamWithUsers]
}
And I've created the Leaf template called "Leaders" which just shows a table with TeamWithUsers per row.
#if(count(teams) > 0) {
<table class="table table-bordered table-hover">
<thead class="thead-light">
<tr>
<th>Name</th>
<th>Icon</th>
<th>Score</th>
</tr>
</thead>
<tbody>
#for(team in teams) {
<tr>
<td>#(team.name)</td>
<td>#(team.icon)</td>
<td>#(team.teamScore)</td>
</tr>
}
</tbody>
</table>
} else {
<h2>There aren’t any teams yet.</h2>
}

EDIT: changed answer in the light of the additional information in the comments.
Whilst you could change your existing function to return the view directly, I personally would leave it as-is and call it from a second that renders the view. Try putting this in the same router as your current function:
func teamsView(_ req: Request) throws -> Future<View> {
let currentID = try req.parameters.next(Int.self)
let teamsContext = getTeamsWithUsersForEvent(request, currentID)
return request.view().render("Leaders", teamsContext )
}
Then, in your routes.swift or equivalent, put:
router.grouped("admin","teams") teamsRoute.get("leaders", Int.parameter, use: teamsView)
Move the parameter into the tsamsview function above and then you need to modify your original function header to be:
func getTeamsWithUsersForEvent(_ req: Request, _ currentID:Int) throws -> Future<[TeamWithUsers]>
Then remove your original parameter extraction from this function.
As your router is grouped your view should be visible on:
http://localhost:8080/admin/teams/nnn/leaders
where nnn is what will get translated into currentID.
ALTERNATIVELY:
If this is getting a bit messy (and in spite of my first comment about preferring to keep the functions separate), you could always alter your existing function to:
func getTeamsWithUsersForEvent(_ req: Request) throws -> Future<View> {
let currentID = try req.parameters.next(Int.self)
print("currentID \(currentID)")
let teamContext = Team.query(on: req).filter(\Team.eventID == currentID).sort(\Team.rawScore, .descending).range(..<5).all().flatMap(to: [TeamWithUsers].self) { team in
try team.map { team in
try team.users.query(on: req).all().map { users in
var score: Int
if users.isEmpty {
score = 0
} else {
score = team.rawScore / users.count
}
return TeamWithUsers(
id: team.id,
name: team.name,
icon: team.icon,
eventID: team.eventID,
rawScore: team.rawScore,
//users: users,
count: users.count,
teamScore: score
)
}
}.flatten(on: req)
}
return request.view().render("Leaders", teamContext )
}

Related

How to do a switchmap or any better way to replace values in RxDart

I have a collection with chatroom information. Something like this:
{
chatroomid: 59,
members: [2,3]
}
Now what I want to do is, get the collection stream, in the course of doing that be able to replace the members string ids with a corresponding firestore document based on member id.
End result should look something like this:
{
chatroomid: 59,
members: [{
id: 2,
username: Johndoe1
},
{
id: 3,
username: Jennydoe1
}]
}
Is this possible with Dart RxDart?
Trying something like this fails:
getChatroomStream(chatroomid)
.switchMap((i) {
return Stream.value(i.members.map((e) => i.members.add(Document(path: 'Global.userRef/$e').streamData())));
})
.listen((event) {print(event);});
[VERBOSE-2:ui_dart_state.cc(177)] Unhandled Exception: Concurrent
modification during iteration: Instance of
'MappedListIterable<dynamic, void>'.
#0 ListIterator.moveNext (dart:_internal/iterable.dart:337:7)
class MemberInfo {
final int id;
final String? username;
final bool isLoading;
}
class Room {
final int chatroomid;
final List<int> members;
}
class RoomWithMemberInfos {
final int chatroomid;
final List<MemberInfo> infos;
factory RoomWithMemberInfos.initial(Room room) {
return RoomWithMemberInfos(
room.chatroomid,
room.members
.map((id) => MemberInfo(id, null, true))
.toList(growable: false)
);
}
RoomWithMemberInfos withInfo(MemberInfo info) {
return RoomWithMemberInfos(
chatroomid,
infos
.map((e) => e.id == info.id ? MemberInfo(e.id, info.name, false) : e)
.toList(growable: false)
);
}
}
Stream<MemberInfo> getMemberInfo(int id) { ... }
getChatroomStream()
.switchMap((Room room) {
final initial = RoomWithMemberInfos.initial(room);
return Stream
.fromIterable(room.members)
.flatMap(getMemberInfo)
.scan<RoomWithMemberInfos>(
(acc, info) => acc!.withInfo(info),
initial,
)
.startWith(initial);
});

How to save the record mutipule table using Asp.net MVC Json

i am creating simple sales system for my final year project. i am creating a sales Form. attached the screen shot image below how the form look like.
after sales completed i need to save the data into multiple table along with the lastinsert id. if i click print invoice button. i have a tables in the database sales,sales product i shown the shot shotimage below.i don't how to save records into multipule table with lastinsert id.
enter image description here
Sales Table
id date subtotal
Sales_Product Table
id sales_id product_id price qty total
Code which i tried
jQuery
function addProject() {
var table_data = [];
$('#product_list tbody tr').each(function (row, tr) {
var sub = {
//these records i am going to add into sales table
'barcode': $(tr).find('td:eq(1)').text(),
'pname': $(tr).find('td:eq(2)').text(),
'pro_price': $(tr).find('td:eq(3)').text(),
'qty': $(tr).find('td:eq(4)').text(),
'total_cost': $(tr).find('td:eq(5)').text(),
};
table_data.push(sub);
});
//these records i am going to add into sales
var total = $("#total").val();
$.ajax({
type: 'POST',
url: '/product/Save',
dataType: 'JSON',
data: {
total: $('#total').val(), data: table_data
},
success: function (data) {
console.log(_data);
var msg;
if (isNew) {
msg = "Sales Completed";
}
last_id = data.last_id
window.location.href = "print.php?last_id=" + last_id;
$.alert({
title: 'Success!',
content: msg,
type: 'green',
boxWidth: '400px',
theme: 'light',
useBootstrap: false,
autoClose: 'ok|2000'
});
isNew = true;
},
error: function (xhr, status, error) {
alert(xhr);
}
});
}
Controller
[HttpPost]
public ActionResult Save(sale s)
{
bool status = false;
if (ModelState.IsValid)
{
using (saleEntities3 dc = new saleEntities3())
{
//Sales table
var v = dc.sales.Where(a => a.id == s.id).FirstOrDefault();
dc.sales.Add(v);
dc.SaveChanges();
v.id = s.id; // lastinsertid
//how to add into lastinsertid as a sales product table as a sales_id colum
//Sales product table i don't how to add
status = true;
}
}
return new JsonResult { Data = new { status = status } };
}
saleEntities3
public partial class saleEntities3 : DbContext
{
public saleEntities3()
: base("name=saleEntities3")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<product> products { get; set; }
public virtual DbSet<sale> sales { get; set; }
public virtual DbSet<sales_product> sales_product { get; set; }
}
}
To save in the Sales_Product table you need to save with the id of the saved sales according to your table structure
[HttpPost]
public ActionResult Save(sale s)
{
bool status = false;
if (ModelState.IsValid)
{
using (saleEntities3 dc = new saleEntities3())
{
//Sales table
var v = dc.sales.Where(a => a.id == s.id).FirstOrDefault();
dc.sales.Add(v);
dc.SaveChanges();
dc.sales_product.Add(new sales_product{
sales_id = s.id,
product_id = s.barcode, //I believe this is the product id
price = s.pro_price,
quantity = s.qty,
total = s.total_cost
});
dc.SaveChanges();
status = true;
}
}
return new JsonResult { Data = new { status = status } };
}

How to reload session values

I am sending data from view to controller using ajax , modelID , catId etc and searching data from database against these parameters, but first time load session values but after that if i change its value cant not load session values.
$("#ModelID").change(function () {
var Cat = $("#Cat").val();
var subcat = $("#CatID").val();
if (Cat != "" & subcat != "") {
$("#modalvalag").modal("show");
$.ajax({
type: 'post',
url: '/Inventory/CatSubCatAgainstModal',
data: {
Cat: $("#Cat").val(),
SubCat: $("#CatID").val(),
ModelID: $("#ModelID").val()
},
success: function (response) {
$('#noo').html(response);
}
});
}
else {
alert("Please select Category Or sub category to complete this process");
}
return false
});
Here is controller code:
public ActionResult CatSubCatAgainstModal(int Cat = 0, int SubCat = 0 , int ModelID = 0)
{
// var dta = db.ItemsCatSubCats.Where(m => m.CatID == CatID & m.SubCatID == SubCatID).FirstOrDefault();
//Session["ItemName"] = dta.Category.Catgory + "|" + dta.SubCategory.SubCat;
Session["CatID"] = Cat;
Session["SubCat"] = SubCat;
Session["ModelID"] = ModelID;
var data = db.ItemAttributes.Where(m => m.CatID == Cat & m.SubCatID == SubCat & m.ModelID == ModelID).ToList();
int c = 0;
foreach(var itm in data){
Session["value"+c] = itm.AttributeValue;
c++;
}
return RedirectToAction("AttributListForValue");
}

Serilog: Usage of additional properties

I'm trying to write additional properties to the log using {Properties} (which are not in the message template):
Used (FileSink) template:
"[{Level}] {Message}{NewLine}{Properties}{NewLine}{Exception}"
Log operation (simplified, normally the object array is given by a method parameter):
Log.Information("Start logging",
new object[]{
new { Version = "VersionString"},
new { StartDate = DateTime.Now },
new { Id = Guid.NewGuid() }
});
I also tired:
Log.Information("Start logging",
new object[]{
"VersionString",
DateTime.Now,
Guid.NewGuid()
});
I had a look on LogEventPropertyCapturingTests and this PR, but I couldn't get it working...
Update
I use wrapper functions like this:
public static void Information(string messageTemplate, object[] propertyValues, bool show = false, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1)
{
using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer }))
{
_MainLog.Information(messageTemplate, propertyValues);
}
if(show)
{
// Code to show a the event to the user
}
}
Update2 Found a way but it's not very nice, as the template-property-matching rudimentary.
public static void Information(string messageTemplate, object[] propertyValues, bool show = false, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1)
{
using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer }))
{
Regex matchProperties = new Regex("{[^}]+}");
int usedPropertiesCount = matchProperties.Matches(messageTemplate).Cast<Match>().Select(m => m.Value).Distinct().Count();
if (propertyValues.Length > usedPropertiesCount)
{
using (LogContext.PushProperty("AdditionalData", propertyValues.Skip(usedPropertiesCount)))
{
_MainLog.Information(messageTemplate, propertyValues);
}
}
else
{
_MainLog.Information(messageTemplate, propertyValues);
}
}
if(show)
{
// Code to show a the event to the user
}
}
The ForContext() method will do this:
Log.ForContext("Version", "VersionString")
.ForContext("Id", Guid.NewGuid())
.Information("Start logging");
(I've omitted StartDate because all Serilog events are already timestamped.)
This blog post series includes some posts on message templates plus context and correlation that cover this plus other alternatives.

run EF6 async request in mvc controller

I have mvc 5 and EF6 and never really use async in mvc before so want ask best way for run sql requests.
public ActionResult SearchTest(string id)
{
string searchtxt = UsefulClass.ConvertObjectToString(id).Replace(",", " ").Trim();
var model = new SearchResult();
if (!String.IsNullOrEmpty(searchtxt))
{
//get the data from 3 requests
var ArtistList = _db.Artists.SqlQuery("SELECT top 6 * FROM Artists WHERE CONTAINS(Name, N'Queen') and TopTracksStatus = 1 and GrabStatus > 1 order by playcount desc").ToList();
var tracks = _db.Database.SqlQuery<TrackInfo>("exec SearchTracks #SearchText=N'Queen', #TopCount=10,#LengthCount=100").ToList();
var TagsList = _db.Tags.Where(x => x.TagName.Contains(searchtxt)).Take(5).ToList();
//work with ArtistList and add to model
if (ArtistList.Any())
{
int i = 0;
foreach (var artist in ArtistList)
{
i++;
if (i == 1) //top artist
{
model.BestArtist = artist;
model.BestArtistTrackCount = _db.TopTracks.Count(x => x.Artist_Id == artist.Id);
}
else
{
model.ArtistList = ArtistList.Where(x => x.Id != model.BestArtist.Id);
break;
}
}
}
//work with tracks and add to model
if (tracks.Any())
{
model.TopTrackList = tracks;
}
//work with tags and add to model
if (TagsList.Any())
{
model.TagList = TagsList;
}
}
return View(model);
}
Here I have 3 requests which return ArtistList, tracks, TagsList and I need add them to model and then pass to view. How to do it in async way?
Try below example, hope it gives you a good idea.
public async Task<ActionResult> SearchTestAsync(string id)
{
string searchtxt = UsefulClass.ConvertObjectToString(id).Replace(",", " ").Trim();
var model = new SearchResult();
if (!String.IsNullOrEmpty(searchtxt))
{
var art = GetArtistListResulst(model);
var track = GetTracksResults(model);
var tag = GetTagsListResults(model,searchtxt);
await Task.WhenAll(art, track, tag);
}
return View(model);
}
private Task GetArtistListResulst(SearchResult model)
{
return Task.Run(() =>
{
var artistList = _db.Artists.SqlQuery("SELECT top 6 * FROM Artists WHERE CONTAINS(Name, N'Queen') and TopTracksStatus = 1 and GrabStatus > 1 order by playcount desc").ToList();
// You don't need to use foreach because the artistList is ordered by playcount already.
model.BestArtist = artistList.Take(1);
model.BestArtistTrackCount = _db.TopTracks.Count(x => x.Artist_Id == model.BestArtist.Id);
model.ArtistList = artistList.Where(x => x.Id != model.BestArtist.Id);
});
}
private Task GetTracksResults(SearchResult model)
{
return Task.Run(() =>
{
model.TopTrackList = _db.Database.SqlQuery<TrackInfo>("exec SearchTracks #SearchText=N'Queen', #TopCount=10,#LengthCount=100").ToList();
});
}
private Task GetTagsListResults(SearchResult model, string searchtxt)
{
return Task.Run(() =>
{
model.TagsList = _db.Tags.Where(x => x.TagName.Contains(searchtxt)).Take(5).ToList();
});
}

Resources