polyhoraire 1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,125 @@
1
+ require 'rubygems'
2
+ require 'google/api_client'
3
+ require 'nokogiri'
4
+ require 'yaml'
5
+ require 'tzinfo'
6
+
7
+ class GoogleExporter
8
+
9
+ def initialize
10
+ auth
11
+ @tz = TZInfo::Timezone.get('America/Montreal')
12
+ @sentEvents = Array.new
13
+ end
14
+
15
+ def send(schedule,toCalID)
16
+ @sentEvents = Array.new
17
+ @sentCalendarID = toCalID
18
+
19
+ schedule.courses.each do |course|
20
+ course.periods.each do |period|
21
+
22
+ courseBeginsOn = schedule.trimester.starts + period.weekDay - 1
23
+ courseBeginsOn += 7 if period.week == 2
24
+ event = {
25
+ 'summary' => course.acronym + '(' + period.group + ') ' + (period.isLab ? '[Lab]' : '') ,
26
+ 'location' => period.location,
27
+ 'timeZone' => 'America/Montreal',
28
+ 'start' => {
29
+ 'dateTime' => dateTime(courseBeginsOn, period.from),
30
+ 'timeZone' => 'America/Montreal'
31
+ },
32
+ 'end' => {
33
+ 'dateTime' => dateTime(courseBeginsOn, period.to),
34
+ 'timeZone' => 'America/Montreal'
35
+ },
36
+ 'recurrence' => [
37
+ 'RDATE;VALUE=DATE:' + rDates(schedule.trimester,period)
38
+ ]
39
+ }
40
+
41
+ result = @client.execute(:api_method => @service.events.insert,
42
+ :parameters => {'calendarId' => toCalID},
43
+ :body_object => event,
44
+ :headers => {'Content-Type' => 'application/json'})
45
+ @sentEvents.push(result)
46
+ end
47
+ end
48
+ end
49
+
50
+ def deleteEvent(eventID,calendarID)
51
+ result = @client.execute(:api_method => @service.events.delete,
52
+ :parameters => {'calendarId' => calendarID, 'eventId' => eventID})
53
+ end
54
+
55
+ def deleteSentEvents
56
+ @sentEvents.each do |event|
57
+ deleteEvent(event.data.id,@sentCalendarID)
58
+ end
59
+ end
60
+
61
+ def selectCalendar(idUrl)
62
+
63
+ end
64
+
65
+ # return : hash[:calendarID => :calendarName]
66
+ def calendarList
67
+ list = Hash.new
68
+
69
+ page_token = nil
70
+ result = client.execute(:api_method => service.calendar_list.list)
71
+ while true
72
+ entries = result.data.items
73
+ entries.each do |e|
74
+ list[e.id] = e.summary
75
+ end
76
+ if !(page_token = result.data.next_page_token)
77
+ break
78
+ end
79
+ result = client.execute(:api_method => service.calendar_list.list,
80
+ :parameters => {'pageToken' => page_token})
81
+ end
82
+ end
83
+
84
+ def auth
85
+ oauth_yaml = YAML.load_file(Poly::userConfDir + '/google-api.yaml')
86
+ client = Google::APIClient.new
87
+ client.authorization.client_id = oauth_yaml["client_id"]
88
+ client.authorization.client_secret = oauth_yaml["client_secret"]
89
+ client.authorization.scope = oauth_yaml["scope"]
90
+ client.authorization.refresh_token = oauth_yaml["refresh_token"]
91
+ client.authorization.access_token = oauth_yaml["access_token"]
92
+
93
+ if client.authorization.refresh_token && client.authorization.expired?
94
+ client.authorization.fetch_access_token!
95
+ end
96
+
97
+ @service = client.discovered_api('calendar', 'v3')
98
+
99
+ @client = client
100
+ end
101
+
102
+ private
103
+
104
+ def dateTime(date, time)
105
+ date = DateTime.parse(date.to_s + ' ' + time)
106
+ date = @tz.local_to_utc(date)
107
+ date.strftime('%FT%TZ')
108
+ end
109
+
110
+ def rDates(trimester,period)
111
+ str = ''
112
+ trimester.getDates(period).each do |date|
113
+ date = DateTime.parse(date.to_s + ' ' + period.from)
114
+ date = @tz.local_to_utc(date)
115
+ str += date.strftime('%Y%m%dT%H%M%SZ,')
116
+ end
117
+ str.chomp(',')
118
+ end
119
+
120
+ def to_calendar(xmlDoc)
121
+ xsl = Nokogiri::XSLT(File.read(Poly::XSLDocs[:exportGoogle]))
122
+ xsl.transform(xmlDoc)
123
+ end
124
+
125
+ end
data/lib/html.xsl ADDED
@@ -0,0 +1,256 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <xsl:stylesheet version="1.0"
3
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fn="http://www.w3.org/2005/xpath-functions"
4
+ xmlns:php="http://php.net/xsl" xmlns:url="http://whatever/java/java.net.URLEncoder"
5
+ xmlns:atom="http://www.w3.org/2005/Atom" xmlns:batch='http://schemas.google.com/gdata/batch'
6
+ xmlns:openSearch='http://a9.com/-/spec/opensearch/1.1/' xmlns:gCal='http://schemas.google.com/gCal/2005'
7
+ xmlns:gd='http://schemas.google.com/g/2005' gd:etag='W/"CkYFSHg-fyp7ImA9WxRVGUo."'
8
+ exclude-result-prefixes="atom gCal openSearch gd fn url php">
9
+
10
+
11
+ <xsl:output encoding="UTF-8" method="html" indent="yes" />
12
+ <xsl:param name="app" />
13
+
14
+ <xsl:template match="/">
15
+ <xsl:apply-templates select="/root/form" />
16
+ <xsl:apply-templates select="/root/atom:feed" />
17
+ <xsl:apply-templates select="/root/linkButton" />
18
+
19
+
20
+ <xsl:element name="ul">
21
+ <xsl:apply-templates select="/root/addedRecently" />
22
+ </xsl:element>
23
+ </xsl:template>
24
+
25
+ <xsl:template match="addedRecently">
26
+ <xsl:if test="position() = 1">
27
+ <xsl:element name="h1">
28
+ <xsl:text>Modification récentes</xsl:text>
29
+ </xsl:element>
30
+ </xsl:if>
31
+ <xsl:element name="li">
32
+ <xsl:value-of select="."/>
33
+ </xsl:element>
34
+ </xsl:template>
35
+
36
+ <xsl:template
37
+ match="atom:feed[atom:id = 'http://www.google.com/calendar/feeds/default/allcalendars/full']">
38
+ <h3>Choix du calendrier</h3>
39
+ <table>
40
+ <xsl:apply-templates select="atom:entry" />
41
+ </table>
42
+ </xsl:template>
43
+
44
+ <xsl:template match="atom:feed"></xsl:template>
45
+
46
+ <xsl:template
47
+ match="atom:feed[atom:id = 'http://www.google.com/calendar/feeds/default/owncalendars/full']">
48
+ <xsl:element name="h1">
49
+ <xsl:text>Étape 2 : Choisir le calendrier</xsl:text>
50
+ </xsl:element>
51
+ <xsl:element name="ul">
52
+ <xsl:apply-templates select="atom:entry" />
53
+ </xsl:element>
54
+ </xsl:template>
55
+
56
+ <xsl:template match="atom:feed[contains(string(atom:id),'/batch/')]">
57
+ <xsl:if test="not(descendant::batch:status/@code = '201')">
58
+ <xsl:variable name="calendarUrl">
59
+ <xsl:value-of select="substring-before(descendant::atom:id, 'batch')" />
60
+ <xsl:text>batch</xsl:text>
61
+ </xsl:variable>
62
+ <xsl:call-template name="linkButton">
63
+ <xsl:with-param name="title">
64
+ Réessayer
65
+ </xsl:with-param>
66
+ <xsl:with-param name="link">?app=PolyHoraire&amp;action=send
67
+ <xsl:value-of select="atom:title" />
68
+ &amp;calendar=
69
+ <xsl:value-of select="$calendarUrl" />
70
+ </xsl:with-param>
71
+
72
+ </xsl:call-template>
73
+ </xsl:if>
74
+ <xsl:element name="table">
75
+ <xsl:apply-templates select="descendant::atom:entry" />
76
+ </xsl:element>
77
+ </xsl:template>
78
+
79
+ <xsl:template match="atom:entry[namespace::batch]">
80
+ <xsl:element name="tr">
81
+ <xsl:element name="td">
82
+ <xsl:value-of select="descendant::atom:title" />
83
+ </xsl:element>
84
+ <xsl:element name="td">
85
+ <xsl:choose>
86
+ <xsl:when test="descendant::batch:status/@code = '201'">
87
+ <xsl:attribute name="class">
88
+ <xsl:text>succes</xsl:text>
89
+ </xsl:attribute>
90
+ <xsl:element name="strong">
91
+ <xsl:text>Réussis</xsl:text>
92
+ </xsl:element>
93
+ </xsl:when>
94
+ <xsl:otherwise>
95
+ <xsl:attribute name="class">
96
+ <xsl:text>failed</xsl:text>
97
+ </xsl:attribute>
98
+ <xsl:element name="strong">
99
+ <xsl:text>Manqué</xsl:text>
100
+ </xsl:element>
101
+ </xsl:otherwise>
102
+
103
+ </xsl:choose>
104
+ </xsl:element>
105
+ </xsl:element>
106
+
107
+ </xsl:template>
108
+
109
+ <xsl:template match="atom:entry">
110
+ <xsl:element name="li">
111
+ <xsl:attribute name="class">
112
+ <xsl:text>linkButton</xsl:text>
113
+ </xsl:attribute>
114
+ <xsl:element name="span">
115
+ <xsl:attribute name="class">
116
+ <xsl:text>gradientBox</xsl:text>
117
+ </xsl:attribute>
118
+ <xsl:attribute name="style">
119
+ <xsl:text>background-color:</xsl:text>
120
+ <xsl:value-of select="gCal:color/@value" />
121
+ <xsl:text>;</xsl:text>
122
+ </xsl:attribute>
123
+ </xsl:element>
124
+ <xsl:element name="a">
125
+ <xsl:attribute name="href">
126
+ <xsl:text>?app=</xsl:text>
127
+ <xsl:value-of select="$app" />
128
+ <xsl:text>&amp;action=send&amp;calendar=</xsl:text>
129
+ <xsl:text>https://www.google.com/calendar/feeds/</xsl:text>
130
+ <xsl:value-of
131
+ select="substring-after(string(atom:id),'http://www.google.com/calendar/feeds/default/owncalendars/full/')" />
132
+ <xsl:text>/private/full/batch</xsl:text>
133
+ <!--
134
+ <xsl:choose>
135
+ <xsl:when test="function-available(url:encode)">
136
+ <xsl:value-of select="url:encode(atom:id)"/>
137
+ </xsl:when>
138
+ <xsl:when test="function-available(fn:encode-for-uri)">
139
+ <xsl:value-of select="fn:encode-for-uri(atom:id)"></xsl:value-of>
140
+ </xsl:when>
141
+ <xsl:otherwise>
142
+ <xsl:value-of select="php:function('urlencode',string(atom:id))"></xsl:value-of>
143
+ </xsl:otherwise>
144
+ </xsl:choose>
145
+ -->
146
+ </xsl:attribute>
147
+ <xsl:value-of select="atom:title" />
148
+ </xsl:element>
149
+ </xsl:element>
150
+ </xsl:template>
151
+
152
+
153
+ <xsl:template match="section[@name='signIn']">
154
+ <form action="?action=getSchedule" method="post">
155
+ <fieldset>
156
+ <legend>Identifiants du dossier étudiant</legend>
157
+ <div>
158
+ <label for="codeAcces">Code d'accèes</label>
159
+ <input type="text" id="codeAcces" />
160
+ </div>
161
+ <div>
162
+ <label for="motDePasse">Mot de passe</label>
163
+ <input type="password" id="motDePasse" />
164
+ </div>
165
+ <div>
166
+ <label for="dateDeNaissance">Date de naissance (AAMMJJ)</label>
167
+ <input type="text" id="dateDeNaissance" />
168
+ </div>
169
+ <button type="submit">Récupérer l'horaire</button>
170
+ </fieldset>
171
+ </form>
172
+ </xsl:template>
173
+
174
+ <xsl:template match="form">
175
+ <xsl:element name="form">
176
+ <xsl:attribute name="action">
177
+ <xsl:text>?app=</xsl:text>
178
+ <xsl:value-of select="$app" />
179
+ <xsl:text>&amp;action=</xsl:text>
180
+ <xsl:value-of select="@action" />
181
+ </xsl:attribute>
182
+ <xsl:attribute name="method">
183
+ <xsl:text>post</xsl:text>
184
+ </xsl:attribute>
185
+ <xsl:apply-templates select="fieldset" />
186
+ </xsl:element>
187
+ </xsl:template>
188
+
189
+ <xsl:template match="fieldset">
190
+ <xsl:element name="fieldset">
191
+ <xsl:element name="legend">
192
+ <xsl:value-of select="@name" />
193
+ </xsl:element>
194
+ <xsl:apply-templates select="input|button" />
195
+
196
+ </xsl:element>
197
+ </xsl:template>
198
+
199
+
200
+ <xsl:template match="input">
201
+ <xsl:element name="div">
202
+ <xsl:element name="label">
203
+ <xsl:attribute name="for">
204
+ <xsl:value-of select="@id" />
205
+ </xsl:attribute>
206
+ <xsl:value-of select="@label" />
207
+ </xsl:element>
208
+ <xsl:element name="input">
209
+ <xsl:attribute name="type">
210
+ <xsl:value-of select="@type" />
211
+ </xsl:attribute>
212
+ <xsl:attribute name="name">
213
+ <xsl:value-of select="@id" />
214
+ </xsl:attribute>
215
+ </xsl:element>
216
+ </xsl:element>
217
+ </xsl:template>
218
+
219
+ <xsl:template match="button">
220
+ <xsl:element name="div">
221
+ <xsl:element name="button">
222
+ <xsl:attribute name="type">
223
+ <xsl:value-of select="@type" />
224
+ </xsl:attribute>
225
+ <xsl:value-of select="." />
226
+ </xsl:element>
227
+ </xsl:element>
228
+ </xsl:template>
229
+
230
+ <xsl:template match="linkButton">
231
+ <xsl:element name="a">
232
+ <xsl:attribute name="href">
233
+ <xsl:value-of select="@href" />
234
+ </xsl:attribute>
235
+ <xsl:attribute name="class">
236
+ <xsl:text>linkButton</xsl:text>
237
+ </xsl:attribute>
238
+ <xsl:value-of select="." />
239
+ </xsl:element>
240
+ </xsl:template>
241
+
242
+ <xsl:template name="linkButton">
243
+ <xsl:param name="link" />
244
+ <xsl:param name="title" />
245
+ <xsl:element name="a">
246
+ <xsl:attribute name="href">
247
+ <xsl:value-of select="$link" />
248
+ </xsl:attribute>
249
+ <xsl:attribute name="class">
250
+ <xsl:text>linkButton</xsl:text>
251
+ </xsl:attribute>
252
+ <xsl:value-of select="$title" />
253
+ </xsl:element>
254
+ </xsl:template>
255
+
256
+ </xsl:stylesheet>
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'net/http'
4
+ require 'uri'
5
+ require 'nokogiri'
6
+
7
+
8
+ module Poly
9
+
10
+ URL = {
11
+ :connection => "https://www4.polymtl.ca/servlet/ValidationServlet",
12
+ :schedule => "https://www4.polymtl.ca/servlet/PresentationHorairePersServlet"
13
+ }
14
+
15
+ XSLDocs = {
16
+ :poly2XML => File.dirname(__FILE__).to_s + "/polyhoraire/poly2XML.xsl",
17
+ :exportGoogle => File.dirname(__FILE__).to_s + "/google.xml.xsl"
18
+ }
19
+
20
+ @userConfDir = 'conf'
21
+
22
+ def self.userConfDir
23
+ @userConfDir
24
+ end
25
+ def self.userConfDir=(value)
26
+ @userConfDir = value
27
+ end
28
+
29
+ def fetch(uri,params)
30
+ url = URI.parse(uri)
31
+
32
+ req = Net::HTTP::Post.new(url.path)
33
+ req.form_data = params
34
+ con = Net::HTTP.new(url.host, url.port)
35
+ con.use_ssl = true
36
+ response = con.start {|http| http.request(req)}
37
+
38
+ case response
39
+ when Net::HTTPSuccess, Net::HTTPRedirection
40
+ return response
41
+ else
42
+ response.error!
43
+ end
44
+ end
45
+
46
+ end
@@ -0,0 +1,106 @@
1
+ require 'polyhoraire'
2
+
3
+ class Poly::Auth
4
+ include Poly
5
+
6
+ def initialize
7
+ @postParams = Hash.new
8
+ @trimesters = Hash.new
9
+ end
10
+
11
+ def connect(user, password, bday)
12
+ params = {
13
+ "code" => user,
14
+ "nip" => password,
15
+ "naissance" => bday }
16
+ response = fetch(Poly::URL[:connection],params)
17
+ extractInformation(response)
18
+ setStatus(response)
19
+ end
20
+
21
+ def connected?
22
+ return @connected
23
+ end
24
+
25
+
26
+ def credentialsValid?(user,password,bday)
27
+ connect(user,password,bday)
28
+ return connected?
29
+ end
30
+
31
+
32
+ # Getters
33
+ def postParams
34
+ return @postParams
35
+ end
36
+ def trimesters
37
+ return @trimesters
38
+ end
39
+ def error
40
+ return @error
41
+ end
42
+
43
+
44
+
45
+ private
46
+
47
+
48
+
49
+ def extractInformation(response)
50
+ doc = Nokogiri::HTML(response.body.to_s)
51
+ @trimesters = extractTrimesters(doc)
52
+ @postParams = extractPostParams(doc)
53
+ end
54
+
55
+ def extractTrimesters(doc)
56
+ trimesterList = doc.xpath("//select[@name = 'selTrimHorPers']/option")
57
+
58
+ trimesters = Hash.new()
59
+ trimesterList.each do |node|
60
+ trimesters[node.get_attribute(:value)] = node.content
61
+ end
62
+ return trimesters
63
+ end
64
+
65
+ def extractPostParams(doc)
66
+ hiddenFields = doc.xpath("//input[@name != 'trimestre']")
67
+
68
+ postParams = Hash.new()
69
+ hiddenFields.each do |node|
70
+ postParams[node.get_attribute(:name)] = node.get_attribute(:value)
71
+ end
72
+ return postParams
73
+ end
74
+
75
+
76
+
77
+
78
+
79
+
80
+ def setStatus(response)
81
+ doc = Nokogiri::HTML(response.body.to_s)
82
+ nodes = doc.xpath("//center/font[@color = '#FF0000']")
83
+ if nodes.empty?
84
+ @connected = true
85
+ else
86
+ @connected = false
87
+
88
+ # Extract the error number and message
89
+ @error = extractError(nodes.first)
90
+ raise "Not connected"
91
+ end
92
+ end
93
+
94
+ def extractError(node)
95
+ error = node.content
96
+ error.delete! ")"
97
+ splitted = error.split("(")
98
+
99
+ error = Hash.new
100
+ error[:number] = splitted[1]
101
+ error[:message] = splitted[0]
102
+
103
+ return error
104
+ end
105
+ end
106
+