Looking for a custom filter than simulates the "Update Project" for status reporting - ms-project

I am currently working with Project Server 2013, PWA and Project 2013 Pro. I am trying to create a dashboard, and require a planned %complete in a project that has no budget, or costs associated with it.
This means I have to create a custom filter that will mimic the Update Project button in the project status tab.
Has anyone else had to do this, or can you provide alternatives I can use?

I wrote something similar to do this in 2010 - I assume it will work in 2013, although can't test it.
Although you don't have any budget or costs, I'm assuming that you do have a baseline with hours against it. That being the case, the following code will output the Planned % Complete (i.e. the proportion of hours you should have burnt on a given task by the status date) to field text11
Option Explicit
Sub CalcBaselinePerctComplete()
Dim pj As Project
Dim t As Task
Dim TSV As TimeScaleValue
Dim TSVs As TimeScaleValues
Dim HrsValue As Double
Dim PerctValue As Integer
Set pj = ActiveProject
For Each t In pj.Tasks
Select Case pj.StatusDate
Case Is > t.BaselineFinish
'If the baseline end date is in the past, then the PerctValue must be 100%
'so no need to loop through the time scaled PerctValues
PerctValue = 100
Case Is < t.BaselineStart
'If the baseline start date is in the future, then the PerctValue must be 0%
'so no need to loop through the time scaled PerctValues
PerctValue = 0
Case Else
If t.BaselineWork = 0 Then
'if there is no baseline work, then the PerctValue must be 1005
'otherwise we've be dividing by zero
PerctValue = 100
Else
Set TSVs = t.TimeScaleData(t.Start, pj.StatusDate, pjTaskTimescaledBaselineWork, pjTimescaleDays)
HrsValue = 0
For Each TSV In TSVs
If TSV.Value <> "" Then
HrsValue = HrsValue + TSV.Value
End If
Next TSV
PerctValue = HrsValue / t.BaselineWork * 100
End If
End Select
SetValue:
t.Text11 = PerctValue & "%"
Next t
End Sub

Related

Conditionally uploading Excel SpreadSheet using FileUpload

Below Is my Vb Code which is getting the row count of the excel file. However, i want to throw an exception and give an error message if the row count is greater than 10k rows. How would i be able to do that? I have done my research on getting the row count but not sure how to throw an exception. The following is an ASP.Net Windows Form Application.
Dim xls As New Excel.Application
Dim sheet As Excel.Worksheet
Dim filePath As String = Path.Combine(GlobalVariable.savedPath, GlobalVariable.excelFileName)
xls.Workbooks.Open(filePath)
sheet = xls.ActiveWorkbook.Sheets(1)
Dim maxSize As Integer = 2
Dim row As Integer = 1
Do Until sheet.Cells(row, 1).value Is Nothing
row += 1
Loop
MsgBox("last Row is " & row - 1)
xls.Workbooks.Close()
xls.Quit()
releaseObject(sheet)
releaseObject(xls)
You are already counting the rows, so you can modify your loop to continue only while row is less then 10,000.
Dim row As Integer = 1
Do
row += 1
Loop while sheet.Cells(row, 1).value IsNot Nothing AndAlso row <= 10000
This will loop, incrementing row until there are no more rows, or if you hit 10,000. You can then check if row is 10,000 to decide if you want to show an error message.
If row >= 10000 then
MsgBox("Over 10000 rows")
'If you want to throw an actual exception you can do:
Throw new Exception("Over 10000 rows")
Else
MsgBox("last Row is " & row - 1)
End If
Updated to reflect OP Question Updates:
I don't know if I would recommend throwing an exception to handle if row reaches 10,000. Instead I would do like so:
Private Sub BtnUpload_OnClick(sender As Object, e As EventArgs) Handles BtnUplaod.Click
REM Save SpreadSheet
Dim filePath As String = Path.Combine(GlobalVariable.savedPath, GlobalVariable.excelFileName)
myfileUploader.saveAs(filePath)
REM Open SpreadSheet
Dim xls As New Excel.Application
Dim sheet As Excel.Worksheet
xls.Workbooks.Open(filePath)
sheet = xls.ActiveWorkbook.Sheets(1)
Dim maxSize As Integer = 2
REM Loop through spreadsheet and count rows
Dim row As Integer = 1
Do
row += 1
Loop While sheet.Cells(row, 1).value IsNot Nothing AndAlso row <= 10000 'Exit loop when end of SpreadSheet is reached or if rows exceeds 10,000
REM Release Resources
xls.Workbooks.Close()
xls.Quit()
releaseObject(sheet)
releaseObject(xls)
REM Decide whether to accept upload, or reject and show error
If row >= 10000 Then 'If row > 10000 delete file and show error
REM Delete the file
File.Delete(filePath)
REM Show some sort of error to user - Its up to you how you want to do so. That is a seperate question
MsgBox("Your Error")
REM Else SpreadSheet is valid
Else
'Your Code code doing what you want with spreadsheet goes here
MsgBox("last Row is " & row - 1)
End If
End Sub
This code will run when the users clicks a button called BtnUpload on the WebPage. You can put it anywhere you like though.
I think this is what you want. Is that correct?

Cannot make Access 2010 accde from Access 2007 accdb

I have a bit of a problem and could really use some help. My organization recently migrated from Office 2007 to Office 2010. I had a database that I developed using Access 2007 (using the .accdb database file type). Throughout the migration process, I was still making updates to my database. All the updates were made via an Office 2007 machine and everything worked on the 2010 systems that I deployed it to, as well as the 2007 boxes. The problem now is that since all the computers are officially on 2010, I cannot seem to create an Accde file from Access 2010. The error I receive is: " The command or action 'MakeMDEFile' isn't available now." * You may be in a read-only database or an unconverted database from an earlier version..." The code is compiled with no errors and my references are good. I have tried to re-compile the code, re-name the wizards in the "C:\Program Files\Microsoft Office\Office14\ACCWIZ" folder and let them re-install, and import all my objects into a new database based on this article: http://msdn.microsoft.com/library/office/dn602608%28v=office.14%29.aspx; all to no avail.I do not have any web content or anything Access 2010 specific, as I only made adjustments to the things I created in 2007. I did read that Access must be compiled on the same version that it was created on, but I thought since both 2007 and 2010 use the .accdb file format it would be compatible? Any advice on this? Thank you.
Thank you for your quick answer! That worked great. I was able to successfully export my DB using the code and I also imported all the objects with the exception of the queries. (I was able to create the accde.) Because I have so many objects, I used a script to import everything. The problem I am experiencing now is with my SQL queries. The export script named the text files a little different for the SQL queries and I don't know how to handle them. Below is my code that worked for the rest of the objects:
Public Sub batchImport_queries()
On Error GoTo batchImport_Err
Dim objFS As Object, objFolder As Object
Dim objFiles As Object, objF1 As Object
Dim strFolderPath As String
strFolderPath = "C:\Users\Me\Desktop\dbexport\queries\"
Set objFS = CreateObject("Scripting.FileSystemObject")
Set objFolder = objFS.GetFolder(strFolderPath)
Set objFiles = objFolder.files
For Each objF1 In objFiles
objF1.Name = Right(objF1.Name, Len(objF1.Name) - 6)'strips "Query_"
objF1.Name = Left(objF1.Name, Len(objF1.Name) - 3) 'strips ".txt"
Application.Application.LoadFromText acQuery, objF1.Name, strFolderPath & objF1.Name
Next
Set objF1 = Nothing
Set objFiles = Nothing
Set objFolder = Nothing
Set objFS = Nothing
batchImport_Exit:
Exit Sub
batchImport_Err:
MsgBox Err.Number & " " & Err.Description
Resume batchImport_Exit
End Sub
That worked for queries like: "Query_qryAvailable.txt" but the SQL ones look like this: "Query_~sq_cCIPSSubform~sq_RosterSubform.txt". It seems to be encapsulating "~sq_c" around the first part of the query name and then the form/subform/or control that is associated with it at the last part of the filename...or I could be completely off. I can't figure out the pattern. Some of them have "~sq_f" instead, only at the leading part.(I'm guessing those are for forms?) Anyway, is there a better way to format the file name (if that's what has to be done) to remove those to my original query names and import correctly? Please let me know if that doesn't make sense. Thank you for your time.
It's possible to export an Access database to text files, see here: http://www.access-programmers.co.uk/forums/showthread.php?t=99179.
Option Compare Database
Option Explicit
Public Sub ExportDatabaseObjects()
On Error GoTo Err_ExportDatabaseObjects
Dim db As Database
'Dim db As DAO.Database
Dim td As TableDef
Dim d As Document
Dim c As Container
Dim i As Integer
Dim sExportLocation As String
Set db = CurrentDb()
sExportLocation = "C:\Temp\" 'Do not forget the closing back slash! ie: C:\Temp\
For Each td In db.TableDefs 'Tables
If Left(td.Name, 4) <> "MSys" Then
DoCmd.TransferText acExportDelim, , td.Name, sExportLocation & "Table_" & td.Name & ".txt", True
End If
Next td
Set c = db.Containers("Forms")
For Each d In c.Documents
Application.SaveAsText acForm, d.Name, sExportLocation & "Form_" & d.Name & ".txt"
Next d
Set c = db.Containers("Reports")
For Each d In c.Documents
Application.SaveAsText acReport, d.Name, sExportLocation & "Report_" & d.Name & ".txt"
Next d
Set c = db.Containers("Scripts")
For Each d In c.Documents
Application.SaveAsText acMacro, d.Name, sExportLocation & "Macro_" & d.Name & ".txt"
Next d
Set c = db.Containers("Modules")
For Each d In c.Documents
Application.SaveAsText acModule, d.Name, sExportLocation & "Module_" & d.Name & ".txt"
Next d
For i = 0 To db.QueryDefs.Count - 1
Application.SaveAsText acQuery, db.QueryDefs(i).Name, sExportLocation & "Query_" & db.QueryDefs(i).Name & ".txt"
Next i
Set db = Nothing
Set c = Nothing
MsgBox "All database objects have been exported as a text file to " & sExportLocation, vbInformation
Exit_ExportDatabaseObjects:
Exit Sub
Err_ExportDatabaseObjects:
MsgBox Err.Number & " - " & Err.Description
Resume Exit_ExportDatabaseObjects
End Sub
If the principle still holds, you should be able to import the resulting objects to a fresh database. If there are any problems with permissions, that should become evident in the text files, but normally this strips all permissions.

I have `Total Number of Seats` and `Available Seats`. How do I manage the two?

I struggled to come up with appropriate title for this thread.
Forgive me it isn't clearcut.
We have a table called Locations with following attributes:
locationID --Each location has 30 capacity seating per class
Capacity_Seating --this is total allowed seat per class per location.
When a user logs in, s/h is presented with a dropdownlist of locations to choose from. Whichever location the trainee chooses, is the location s/he is going to take the training at.
The Capacity seating for each location is 30.
As soon as the user logs in, s/he is taken to the Trainring page. The training displays general information about the classes, including the date and time of training, duration, the Capacity Seating and most importantly, available seats or Seats remaining.
If seats are still available, the user can click Register to register for that particular training.
Once this user is registered, the available seat changes.
For instance, if there were 15 seats prior to this trainee registering, then after registering, the available seating with now read 14 seats.
If a user chooses to cancel his or her seat after initially registering, the trainee can do so as long as it isn't within 24 hours of training date.
Here are my questions.
1, do I need to add another field called Available_Seats to the location table or to the Training table to show how many seats remain or can this be done using a query like:
Select (Capacity_Seating - each time trainee registers)?? Not sure how to handle this.
2, We would like to use register to substract a number from Capacity_Seating and Cancel to put back a number to Capacity_Seating.
Your thoughts and assistance are greatly appreciated.
<ItemTemplate>
<asp:LinkButton ID="Btncalc" runat="server" Text="Register" tooltip="Click to calculate" onclick="calc" />
</ItemTemplate>
</asp:TemplateField>
</ItemTemplate>
Sub calc(ByVal sender As Object, ByVal e As System.EventArgs)
Dim objConnection As SqlConnection
Dim username = Session.Item("Username").ToString
' Dim strSQL As String
objConnection = New SqlConnection(ConfigurationManager.ConnectionStrings("DBConnectionString").ConnectionString)
objConnection.Open()
strSQL = "update TrainingTable set employeeId='" & username
strSQL += "', AvailableSeats= AvailableSeats-1"
strSQL += " where location = '" & ddlLocation.SelectedValue.ToString & "'"
'Response.Write(strSQL)
'Response.End()
Dim cmdcommand As New SqlCommand(strSQL, objConnection)
cmdcommand.ExecuteNonQuery()
cmdcommand = Nothing
objConnection.Close()
objConnection = Nothing
End Sub
By the way, markup is on gridview.
For your first question how you would like to proceed mostly comes down to what you would like to do. Having another field for the available seats in a table or finding the remaining seats using a query are both viable options. The benefits or pitfalls of either are really negligible given the basic structure of your system. Seeing as you would use a specific query to find this piece of information constantly it would be better practice and sense to add this value in as a new field in the table. This will eliminate the need for a specific query and make this data more public to other systems and queries.
For your second question I'm not quite sure I follow it but I'll try to give you my best opinion. It seems you want to alter the total capacity field from each table based on registrations and cancellations which I would disagree with. The capacity value shouldn't be modified if the actually total capacity of location does not change. So rather than changing the total capacity through cancels or registrations I would change that new available seats field. This leaves no chance for confusion when viewing the capacity of the location and will easily allow you to find the available spaces for registration.
This is a simple module:
It gives you a choice of a particular course, with two venues and an option of reg or adv course at each venue (this doesn't affect the seating)
I have NOT included the SQL OR server side scripts, at this stage.. this algorithm should help.
You don't need to add another column to your table, just update the fields as the program is implemented, and you can delete a row, if it is cancelled. I haven't included code for that.. (it's too long!!! hahaha)
(NB this doesn't error check for datatype - it's a simple format to get an idea)
Module Module1
Sub main()
Dim Seat As Object
Dim Course As Object
Dim Person1 As Integer
Dim Venue1 As Integer
Dim Venue2 As Integer
Dim regular As Integer
Dim Advanced As Integer
Dim x As String
Do
Person1 = Person1 + 1
If Venue1 = 30 Then
Console.WriteLine("Venue1 is full")
End If
If Venue2 = 30 Then
Console.WriteLine("Venue2 is full")
End If
Console.WriteLine("enter 1 for Venue1, 2 for Venue2")
Seat = Console.ReadLine
If Seat = 1 Then
Venue1 = Venue1 + 1
Else
Venue2 = Venue2 + 1
End If
Console.WriteLine("Regular course = 1, Advanced course = 2")
Course = Console.ReadLine
If Course = 1 Then
regular = regular + 1
Else
Advanced = Advanced + 1
End If
Console.WriteLine("Press enter to continue, r for reports or x to quit")
x = Console.ReadLine
If x = "r" Then
Console.WriteLine("total Persons= " & Person1 & vbCrLf & "total Venue1= " & Venue1 & vbCrLf & "total Venue2=" & Venue2 & vbCrLf & "Course: regular " & regular & vbCrLf & "Advanced " & Advanced)
ElseIf x = "x" Then
Exit Do
Else
End If
Loop Until Person1 = 60
If Person1 = 60 Then
Console.WriteLine("Course is full.")
End If
End Sub
End Module
Please let me know if you need any more help or clarity.

How to improve the performance of this function?

This function takes around 1.2 seconds to execute. I am unable to understand why? Is it because of the inner joins? If yes, then how can i improve the execution speed? I am using Microsoft Enterprise Library.
Public Shared Function GetDataByInterests(ByVal accountId As Integer) As Object
Dim details As New List(Of GetIdBasedOnInterest)()
Dim getIDs As New GetIdBasedOnInterest
Dim interests As String = ""
Dim db As SqlDatabase = Connection.Connection
Using cmdGeneric As DbCommand = db.GetSqlStringCommand("SELECT Interests.InterestName FROM UserInterests INNER JOIN Interests ON UserInterests.InterestID = Interests.InterestID WHERE UserInterests.AccountID=#AccountID")
db.AddInParameter(cmdGeneric, "AccountID", SqlDbType.Int, accountId)
Dim dsInterests As DataSet = db.ExecuteDataSet(cmdGeneric)
For i = 0 To dsInterests.Tables(0).Rows.Count - 1
If i = dsInterests.Tables(0).Rows.Count - 1 Then
interests = interests & dsInterests.Tables(0).Rows(i).Item(0).ToString
Else
interests = interests & dsInterests.Tables(0).Rows(i).Item(0).ToString & ","
End If
Next
End Using
getIDs.InterestName = interests
details.Add(getIDs)
Return details
End Function
Without knowing anything of the underlying tables and their indexes (and this is a check you should do immediately) there is an obvious problem in your loop.
You cancatenate strings, this, potentially could pose a strong pressure on the memory used by your program.
A string concatenation results in a new string allocated on the memory and thus, if your table contains many rows, the effect could be noticeable.
You could try to use a StringBuilder
Dim interests As new StringBuilder(1024) ' suppose an internal buffer of 1K'
...
If i = dsInterests.Tables(0).Rows.Count - 1 Then
interests.Append(dsInterests.Tables(0).Rows(i).Item(0).ToString)
Else
interests.Append(dsInterests.Tables(0).Rows(i).Item(0).ToString & ",")
End If
....
getIDs.InterestName = interests.ToString
Of course this optimization could be absolutely not important if your tables (UserInterests and Interests) are not correctly indexed on the fields InterestID and AccountID
EDIT: Another micro-optimization is to remove the internal IF test and truncate the resulting output only after the loop ends
For ....
interests.Append(dsInterests.Tables(0).Rows(i).Item(0).ToString & ",")
Next
if(interest.Length > 0) interest.Length -= 1;
EDIT As for your request, this is an example to create an unique index. The syntax could be more complex and varying depending on the Sql Server version, but basically you do this in Sql Management Studio
CREATE UNIQUE INDEX <indexname> ON <tablename>
(
<columntobeindexed>
)
Check the CREATE INDEX statement examples on MSDN
1) Time your query in SQL Server Management studio. It will be much easier to tune it there in isolation from your VB code. Also you can run the display the query plan, and it ight even suggest new indexes.
2) Check you have the relevent primary keys and indexes defined.
3) Pull common expressions out of your for loop, to avoid recomputing the same thing over and over:
4) Like Steve says, use a StringBuilder
Combining those points:
Dim theTable as ...
Dim rowCount as Integer
Dim interests As new StringBuilder(1024)
Set theTable = dsInterests.Tables(0)
rowCount = theTable.Rows.Count
For i = 0 To rowCount - 1
interests.Append(theTable.Rows(i).Item(0).ToString)
If i <> rowCount - 1 Then
interests.Append(",")
End If
Next

Filter taking 2 seconds on small adbodb recordset

I have a small adodb recordset I am trying to filter. This one is 6 records for our test customer.
For some reason the filter is taking 2 seconds to complete, and I am doing this around 30 times on my asp page. Thus, making my page really slow to load. The other recordset filters on this page are running fast.
I have tried setting different CursorLocations and CursorTypes..
Can anyone help me determine why this filter is so slow?
rsAvgPrice.Filter = "CommodityID = 13 AND CropYear = '12'"
Probably the whole query is executed again and only then the filter is being applied.
I would have one single loop over all the items, store the required data in local variables then have my own filter. Best efficiency, much better control.
For example, if you want the data filtered by those two fields, I would use Dictionary like this:
Dim oCommodity_CropYear_Data, curKey
Dim curCommodityID, curCropYear, curData
Set oCommodity_CropYear_Data = Server.CreateObject("Scripting.Dictionary")
Do Until rsAvgPrice.EOF
curCommodityID = rsAvgPrice("CommodityID")
curCropYear = rsAvgPrice("CropYear")
curKey = curCommodityID & "_" & curCropYear
curData = "field1: " & rsAvgPrice("somefield") & ", field 2: " & rsAvgPrice("other_field") & "<br />"
oCommodity_CropYear_Data(curKey) = oCommodity_CropYear_Data(curKey) & curData
rsAvgPrice.MoveNext
Loop
rsAvgPrice.Close
Then to extract the data in a loop:
For x=1 To 30
For y=1 To 12
curKey = x & "_" y
If oCommodity_CropYear_Data.Exists(curKey) Then
Response.Write("Data for Commodity " & x & " and CropYear " & y & ":<br />" & oCommodity_CropYear_Data(curKey)
End If
Next
Next
This is the general idea, hope you can use it for your actual needs.
I have resolved this issue.
The issue was when I declare a record set the following way, the cursor type gets set as adOpenForwardOnly and the cursor location to adUseServer. These settings cannot be changed if you fill your recordset using command.Execute.
Set cmd = Server.CreateObject("ADODB.Command")
cmd.CommandType = adCmdText
cmd.CommandText = mySQL
cmd.CommandTimeout = 3000
cmd.ActiveConnection = cn
Set rs = Server.CreateObject("ADODB.Recordset")
Set rs = cmd.Execute
Set cmd = Nothing
The way I resolved this was manually declaring a permanent recordset with its fields. Then I filled a temporary recordset using the command.execute. I then manually populated my declared recordset with the temporary recordset record by record. This allowed me to set the cursorlocation to adUseClient.
Thus speeding up the filter by leaps and bounds.

Resources