Attachment 'sctable-1.3.5-4.py'

Download

   1  
   2 """
   3     MoinMoin - sctable a Processor for spread sheet calculations by sc
   4     @license: GNU GPL, see COPYING for details.
   5 
   6     PURPOSE:
   7         This processor is used to do some spread sheet calculation based on sc in a
   8 	regular wiki table. The first column/first line coordinate is A0.
   9 
  10     CALLING SEQUENCE:
  11        {{{
  12        #!sctable [-column_header, -row_header, -show_formular, -format  ]
  13        }}}
  14 
  15     OPTIONAL INPUTS:
  16        -column_header: additional in the result the column header is shown
  17        -row_header: additional in the result the line number header is shown
  18        -show_formular: if set the formular instead of the result is shown,
  19                        data is arranged in textmode. Blanks in formulars are removed
  20        -format: is used to the set the number of digits for the column values
  21 
  22 
  23     EXAMPLE:
  24 {{{
  25 SUM over columns}}}
  26 {{{
  27 #!sctable
  28 ||1||2||=A0+B0||
  29 ||10||20||=@sum(A1:B1)||
  30 }}}
  31 
  32 RESULT:
  33 ||<)>1.00||<)>2.00||<)>3.00||
  34 ||<)>10.00||<)>20.00||<)>30.00||
  35 ------
  36 {{{
  37 cell B1 no data}}}
  38 
  39 {{{
  40 #!sctable
  41 ||A||B||C||D||
  42 ||1||||2||=A1+C1||
  43 }}}
  44 
  45 RESULT:
  46 ||<(>A||<(>B||<(>C||<(>D||
  47 ||<)>1.00||<)>||<)>2.00||<)>3.00||
  48 
  49 -----
  50 {{{
  51 SUM over rows}}}
  52 {{{
  53 #!sctable
  54 ||1||2||=A0+B0||
  55 ||10||20||30||
  56 ||=@sum(A0:A1)||=@sum(B0:B1)||=@sum(C0:C1)||
  57 }}}
  58 
  59 RESULT:
  60 ||<)>1.00||<)>2.00||<)>3.00||
  61 ||<)>10.00||<)>20.00||<)>30.00||
  62 ||<)>11.00||<)>22.00||<)>33.00||
  63 
  64 -----
  65 {{{
  66 SUM over rows and columns}}}
  67 {{{
  68 #!sctable
  69 ||A||B||C||
  70 ||1||2||=A1+B1||
  71 ||10||20||=@sum(A2:B2)||
  72 ||=@sum(A1:A2)||=@sum(B1:B2)||=@sum(C1:C2)||
  73 }}}
  74 
  75 RESULT:
  76 ||<(>A||<(>B||<(>C||
  77 ||<)>1.00||<)>2.00||<)>3.00||
  78 ||<)>10.00||<)>20.00||<)>30.00||
  79 ||<)>11.00||<)>22.00||<)>33.00||
  80 
  81 -----
  82 {{{
  83 -column_header}}}
  84 {{{
  85 #!sctable  -column_header
  86 ||1||2||
  87 ||3||4||
  88 ||5||6||
  89 }}}
  90 
  91 RESULT:
  92 ||<:#CCCCCC>'''A'''||<:#CCCCCC>'''B'''||
  93 ||<)>1.00||<)>2.00||
  94 ||<)>3.00||<)>4.00||
  95 ||<)>5.00||<)>6.00||
  96 
  97 
  98 -----
  99 {{{
 100 -row_header}}}
 101 {{{
 102 #!sctable -row_header
 103 ||1||2||
 104 ||3||4||
 105 ||5||6||
 106 }}}
 107 
 108 RESULT:
 109 ||<)5%#CCCCCC>'''0'''||<)>1.00||<)>2.00||
 110 ||<)5%#CCCCCC>'''1'''||<)>3.00||<)>4.00||
 111 ||<)5%#CCCCCC>'''2'''||<)>5.00||<)>6.00||
 112 
 113 -----
 114 {{{
 115 -column_header  -row_header}}}
 116 {{{
 117 #!sctable  -column_header  -row_header
 118 ||1||2||
 119 ||3||4||
 120 ||5||6||
 121 }}}
 122 
 123 RESULT:
 124 ||<:5%#CCCCCC> ||<:#CCCCCC>'''A'''||<:#CCCCCC>'''B'''||
 125 ||<)5%#CCCCCC>'''0'''||<)>1.00||<)>2.00||
 126 ||<)5%#CCCCCC>'''1'''||<)>3.00||<)>4.00||
 127 ||<)5%#CCCCCC>'''2'''||<)>5.00||<)>6.00||
 128 
 129 -----
 130 {{{
 131 -show_formular  -column_header  -row_header}}}
 132 {{{
 133 #!sctable -show_formular  -column_header  -row_header
 134 ||m||p||
 135 ||1||=A1 * 5||
 136 ||2||=A2-3||
 137 ||3||4||
 138 }}}
 139 
 140 RESULT:
 141 ||<:5%#CCCCCC> ||<:#CCCCCC>'''A'''||<:#CCCCCC>'''B'''||
 142 ||<)5%#CCCCCC>'''0'''||<(>m||<(>p||
 143 ||<)5%#CCCCCC>'''1'''||<(>1||<(>=A1*5||
 144 ||<)5%#CCCCCC>'''2'''||<(>2||<(>=A2-3||
 145 ||<)5%#CCCCCC>'''3'''||<(>3||<(>4||
 146 
 147 -----
 148 {{{
 149 -column_header and blanks in cells}}}
 150 {{{
 151 #!sctable -column_header
 152 ||Name Vorname||  ||  || 3 || || 5||
 153 ||Name Vorname|| 1 || 2 ||  || 4 || 5||
 154 ||Name Vorname|| 1 || 2 || || || 5||
 155 }}}
 156 
 157 RESULT:
 158 ||<:#CCCCCC>'''A'''||<:#CCCCCC>'''B'''||<:#CCCCCC>'''C'''||<:#CCCCCC>'''D'''||<:#CCCCCC>'''E'''||<:#CCCCCC>'''F'''||
 159 ||<(>Name Vorname||    ||    ||<)>3.00||    ||<)>5.00||
 160 ||<(>Name Vorname||<)>1.00||<)>2.00||    ||<)>4.00||<)>5.00||
 161 ||<(>Name Vorname||<)>1.00||<)>2.00||    ||    ||<)>5.00||
 162 
 163 -----
 164 {{{
 165 -format 1,1}}}
 166 {{{
 167 #!sctable -format 1,1
 168 ||1||2||
 169 ||3||4||
 170 ||=@sum(a0:a1)||=a2*4||
 171 }}}
 172 
 173 RESULT:
 174 ||<)>1.0||<)>2.0||
 175 ||<)>3.0||<)>4.0||
 176 ||<)>4.0||<)>16.0||
 177 
 178 -----
 179 {{{ useage of variable names -show_formular -column_header  -row_header
 180 }}}
 181 {{{#!sctable -show_formular -column_header  -row_header
 182 ||A||B||C||
 183 ||1||{two}2||=A1+two||
 184 ||10||20||=@sum(A2:B2)||
 185 ||=@sum(A1:A2)||=@sum(B1:B2)||=@sum(C1:C2)||
 186 }}}
 187 
 188 
 189 RESULT:
 190 ||<:5%#CCCCCC> ||<:#CCCCCC>'''A'''||<:#CCCCCC>'''B'''||<:#CCCCCC>'''C'''||
 191 ||<)5%#CCCCCC>'''0'''||<(>A||<(>B||<(>C||
 192 ||<)5%#CCCCCC>'''1'''||<(>1||<(>{two}2||<(>=A1+two||
 193 ||<)5%#CCCCCC>'''2'''||<(>10||<(>20||<(>=@sum(A2:B2)||
 194 ||<)5%#CCCCCC>'''3'''||<(>=@sum(A1:A2)||<)>=@sum(B1:B2)||<(>=@sum(C1:C2)||
 195 
 196 and if we calculate [[BR]]
 197 RESULT:
 198 {{{#!sctable -column_header  -row_header
 199 ||A||B||C||
 200 ||1||{two}2||=A1+two||
 201 ||10||20||=@sum(A2:B2)||
 202 ||=@sum(A1:A2)||=@sum(B1:B2)||=@sum(C1:C2)||
 203 }}}
 204 -----
 205 {{{color in cells
 206 }}}
 207 {{{#!sctable
 208 ||<:rowbgcolor=lightcyan>'''A'''||<:>'''B'''||<:>'''C'''||
 209 ||<)#dddddd>1||<)#dddddd>{two}2||<)#cccccc>=A1+two||
 210 ||<(>10||<)>20||<:#dddddd>=@sum(A2:B2)||
 211 ||<rowbgcolor="#cc99ff">=@sum(A1:A2)||=@sum(B1:B2)||<bgcolor=magenta>=@sum(C1:C2)||
 212 }}}
 213 
 214 RESULT:
 215 ||<:rowbgcolor=lightcyan>'''A'''||<:>'''B'''||<:>'''C'''||
 216 ||<)#dddddd>1.00||<)#dddddd>2.00||<)#cccccc>3.00||
 217 ||<(>10.00||<)>20.00||<:#dddddd>30.00||
 218 ||<rowbgcolor="#cc99ff">11.00||22.00||<bgcolor=magenta>33.00||
 219 -----
 220 
 221 
 222     PROCEDURE:
 223       This processor needs the external sc (http://freshmeat.net/projects/sc/) routine.
 224       It is necessary to have a tmp directory in the wiki data dir.
 225       All formulars have to start by a "=" sign.
 226 
 227       Please remove the version number from the routine name!
 228 
 229     RESTRICTIONS:
 230       * german umlauts e.g. äöü are not implemented by now. 
 231         they need to be masked because sc is not able to proceed calculation with such signs.
 232 	 
 233       
 234 
 235     MODIFICATION:
 236        @copyright: 2004-09-19 by Reimar Bauer (R.Bauer@fz-juelich.de) sctable-1.2.3-1
 237        1.2.3-2 : (RB) bug fixed line #!sctable was not found by giving input parameters
 238        1.2.3-3 : (RB) input parameter -show_formular added, column width is set to 200 chars
 239                :      if this parameter is used
 240                : (RB) bug removed (already) #!sctable position could be different from 0
 241 	       :      but always greater -1
 242       1.2.3-4  : 2004-10-05 RB format always extended for sc call to 200 signs.
 243                  bug with blanks in names removed, more as one blank in a cell handled as one blank.
 244       1.2.3-5  : 2004-10-16 RB format codes for colors and cells removed before calculations
 245                  -format optional input var added
 246 
 247       1.3      : 2004-11-13 RB changed to PARSER
 248                  bug fixed: strings with blanks are formatted to the left
 249 		 feature added: column width of row numbers is set to 5%
 250 		 some examples fixed
 251 
 252       1.3.3-2 patch and examples from towi AT geocities DOT SPAM com implemented
 253               : format and colors are used by now!!
 254 
 255       	FUNCTIONAL ADDITIONS
 256 
 257   	* a cell can now start with an wiki format string that will format
 258     	  the cell according to wiki table formatting,
 259           examples:
 260           <:>@sum(A3:a6)
 261           <rowbgcolor=cyan>Title
 262 
 263        * you can define a name for a cell and use it later (no range defines yet):
 264          when combinign with <format> the format must come first.
 265          examples:
 266          {income}20000
 267          <:>{outcome}=income/2
 268 
 269     DISCUSSION (need help):
 270       * we need a better routine to destinguish between strings and numbers, i have still some problems with e.g
 271         def is_number(txt):
 272         	ItIs = True
 273         try:
 274                 f =float(txt)
 275         except TypeError:
 276                 ItIs = False
 277 
 278         return(ItIs)
 279 
 280 	This doesn't work with formulars =@sum(a0:a2)
 281 
 282         what I do at the moment is to scan digits and signs from a string. This does not right identify a dot (.)
 283 	in a string. This will be formatted as number. Thats the only bad thing I know on.
 284 
 285 	ideas are welcome.
 286 
 287         1.3.5-3: 2005-08-05 RB tmp path set relativ to installation and created if it isn't there
 288 	                       space before and after a cell entry by now ignored
 289                 
 290 
 291 
 292 """
 293 
 294 Dependencies = []
 295 import sys, os, re, sha
 296 from MoinMoin.parser import wiki
 297 from MoinMoin.action import AttachFile
 298 from MoinMoin.Page import Page
 299 import string,os
 300 
 301 config_external_sc="/usr/local/bin/sc"
 302 
 303 def table2sc(lines,format,show_formular,request):
 304 
 305 	result=[]
 306 	formats = {} # { (row,col): '<wikiformat>', ... }
 307 
 308 	r=0
 309 	name="=" # searchstring
 310 	col_names=' ABCDEFGHIJKLMNOPQRSTUVWXYZ'
 311 
 312 	first_line=lines[0]
 313 	col_list=first_line.split('||')
 314         digit="2"
 315 	c=0
 316 	form=[]
 317 
 318 	for value in col_list:
 319 		if (len(value.lstrip()) > 0):
 320   			if (format=="") :
 321               			digit="2"
 322 	   		else:
 323 	      			digit=format[c-1]
 324 
 325            		f="%(command)s %(column)s %(arg)s" %{
 326 	     			"command":"format",
 327 	     			"column":col_names[c],
 328 	     			"arg":"200 " + digit + " 0"
 329 	     		}
 330 	   		form.append(f)
 331 
 332         	c=c+1
 333     	result.append(form)
 334 
 335 
 336 	for txt in lines:
 337 		n_name=txt.count(name)
 338 		txt=txt.lstrip()
 339 		
 340 		sargs=txt.split('||')
 341 		n=len(sargs)
 342 		sargs=sargs[0:n-1]
 343                 
 344 		c=0      # linecounter
 345 		digits="=-.0123456789"
 346 		for arg in sargs:
 347 		        
 348 		        arg=arg.strip()
 349 			
 350 			# check for wiki formatting string at beginning of sc data: "<format>..."
 351          		if arg.startswith('<'):
 352             			p = arg.find('>')+1
 353             			if p>1:
 354                 			formats[(r,c)] , arg = arg[:p] , arg[p:]
 355 					arg=arg.strip()
 356 			# check for a sc column name define: "{defname}..."
 357          		defname = None
 358 			if (show_formular == 0):
 359          			if arg.startswith('{'):
 360             				p = arg.find('}')
 361             				if p>0:
 362                 				defname , arg = arg[1:p] , arg[p+1:]
 363 						arg=arg.strip()
 364 
 365 			# sc data
 366         		if (len(arg) > 0):
 367 	    			if arg[0] in digits:
 368 	       				if (arg.find(name) == 0):
 369                   				if (show_formular == 0):
 370                       					sargs[c]="%(command)s %(column)s %(arg)s" %{
 371 						        	"command":'let ',
 372 					      			"column":col_names[c]+str(r),
 373 			      					"arg":string.strip(arg)
 374 			    				}
 375                   				else:
 376 		      					arg=string.replace(arg,"="," =")
 377                       					sargs[c]="%(command)s %(column)s=%(arg)s" %{
 378 		              					"command":'leftstring',
 379 			      					"column":col_names[c]+str(r),
 380 			      					"arg":'"'+string.replace(arg,' ','')+'"'
 381 			    				}
 382 
 383 	       				else:
 384 	         				if (show_formular == 0):
 385                     					sargs[c]="%(command)s %(column)s=%(arg)s" %{
 386 		              						"command":'let ',
 387 			      						"column":col_names[c]+str(r),
 388 			      						"arg":string.strip(arg)
 389 			     				}
 390 		 				else:
 391                     					sargs[c]="%(command)s %(column)s=%(arg)s" %{
 392 			  						"command":'leftstring',
 393 									"column":col_names[c]+str(r),
 394 			      						"arg":'"'+string.replace(arg,' ','')+'"'
 395 								}
 396 
 397 
 398             			else:
 399 	       				if (c > 0):
 400 						if (string.strip(arg) == ""):
 401 		      					arg="Blank!@"
 402 		  				else:
 403 		      					arg=string.strip(arg)
 404 		      					arg=string.replace(arg," ","Blank!@")
 405 
 406                   				sargs[c]="%(command)s %(column)s=%(arg)s" %{
 407 		              				"command":'leftstring ',
 408 			      				"column":col_names[c]+str(r),
 409 			      				"arg":'"'+arg+'"'
 410 			    			}
 411 
 412 			else:
 413 				if (c > 0):
 414 					sargs[c]="%(command)s %(column)s=%(arg)s" %{
 415 		              			"command":'leftstring ',
 416 				      		"column":col_names[c]+str(r),
 417 				      		"arg":'" Blank!@"'
 418 			    		}
 419 			if defname:
 420 				sargs[c] = '%s\ndefine "%s" %s%s' % (sargs[c], defname, col_names[c], r)
 421 			c=c+1
 422 		result.append(sargs)
 423 		r=r+1
 424 
 425 	return(result,formats)
 426 
 427 class Parser:
 428 
 429         extensions = ['.sc']
 430 	
 431 	
 432 	
 433 	def __init__(self, raw, request, **kw):
 434 
 435         	self.raw = raw
 436         	self.request = request
 437         	self.form = request.form
 438         	self._ = request.getText
 439 		self.kw = []
 440 		for arg in kw.get('format_args','').split():
 441 		   self.kw.append(arg)
 442 
 443 
 444 
 445 
 446 
 447 	def format(self, formatter):
 448                 config_sc_vartmp_dir = os.path.join(self.request.rootpage.getPagePath(),'tmp')
 449 		
 450 		if not os.path.exists(config_sc_vartmp_dir):
 451 		   os.mkdir(config_sc_vartmp_dir)
 452 
 453 		   
 454 		
 455 		lines = self.raw.split('\n')
 456 		
 457 		zt=0
 458 		for txt in lines:
 459 		    
 460 		    txt=txt.replace(u'\xc3\x84','AE')
 461 		    lines[zt]=txt
 462 		    zt=zt+1
 463 		
 464 		kw=self.kw
 465 
 466     		column_header=0
 467     		row_header=0
 468     		show_formular=0
 469     		format=''
 470 
 471     		zt=0
 472 
 473     		for test in kw:
 474       			if (test == '-column_header') : column_header=1
 475       			if (test == '-row_header') : row_header=1
 476       			if (test == '-show_formular') : show_formular=1
 477       			if (test == '-format') : format=string.split(kw[zt+1],",")
 478       			zt=zt+1
 479 
 480 
 481     		matrix = []
 482 
 483     		textstr = '\n'.join(lines).strip()
 484 
 485     		tmpname = re.sub('\s+', ' ', textstr)
 486 		
 487 		
 488 		tmpname=sha.new(tmpname.encode('utf8','replace')).hexdigest().upper()
 489     		
 490 		#tmpname=sha.new(tmpname).hexdigest()
 491 		
 492 		
 493     		tmpname = tmpname + "_sc"
 494     		tmpfile = "%s/%s.sc" % (config_sc_vartmp_dir, tmpname)
 495 		
 496 		
 497 		textstr, formats=table2sc(lines,format,show_formular,self.request)
 498 		
 499 
 500 
 501     		data = open(tmpfile, "w")
 502     		
 503     		i=0
 504     		for txt in textstr:
 505         		
 506 			if (len(textstr[i]) > 0):
 507             			tmpstr=string.join(textstr[i],'\n')
 508 			        data.write('%s\n' % tmpstr.encode('latin-1'))
 509         		i=i+1
 510     		data.close()
 511 		
 512 
 513     		cmd = "%(external_sc)s %(argument)s %(file)s " % {
 514              		"external_sc": config_external_sc,
 515              		"argument": " -W% ",
 516 	     		"file":tmpfile
 517           		}
 518 
 519 
 520     		f=os.popen(cmd,'r')# popen to get the result of the calculation
 521 		sys.stderr.write(cmd)
 522     		result=f.readlines()
 523     		#f.flush()
 524     		os.unlink(tmpfile) # remove the tmpfile
 525 
 526     		right_format='<)>'
 527     		left_format='<(>'
 528 
 529     		for txt in result:
 530 		        
 531       			txt=string.join(txt,'')
 532       			cells=string.split(txt,' ')
 533       			zres=[]
 534       			c=1
 535       			for value in cells:
 536         			value=string.join(value,'')
 537         			value=string.strip(value)
 538         			strlen=len(value)
 539 
 540 				if (strlen > 0) :
 541 					v = ord(value[0])
 542 	   				if ord('a')<=v<=ord('z') or ord('A')<=v<=ord('Z') or value[0] in " ":
 543 						format=left_format
 544 	   				else:
 545 	      					if (show_formular == 0) :
 546 	         					format=right_format
 547 	      					else:
 548               			 			format=left_format
 549 
 550            				if (value == "Blank!@"):
 551 						value=" "
 552 
 553 	   				if string.find(value,"Blank!@"):
 554 	      					value=string.replace(value,"Blank!@", " ")
 555 
 556 					zres.append( [format, value] )
 557 
 558 					c=c+1
 559       			matrix.append(zres)
 560 			# post formatting of cells according to cut out wiki format strings
 561     			# - matrix is here: [ [  [format,content], ... ], ... ]
 562     		for cellname, cellformat in formats.items():
 563         		row,col = cellname
 564         		m = matrix[row][col-1]
 565 			m[0] = cellformat
 566     		matrix = map(lambda row: "||" + "||".join([f+c for f,c in row]) + "||", matrix)
 567 
 568     		# post processing of over all matrix
 569     		# - matrix is here: [ "||cell||cell||...||", "||cell||...||", ... ]
 570 
 571     		if (len(matrix[0]) == 2):  # das ist noch nicht die beste Loesung (workaround)
 572        			matrix=matrix[1:]
 573 
 574     		if (row_header == 1):
 575       			y=[]
 576       			r=len(matrix)
 577       			lines=range(r)
 578 
 579       			i=0
 580 
 581       			for no in lines:
 582 			        matrix[i]="||<)5%#CCCCCC>'''"+str(no)+"'''"+matrix[i]
 583 		 		i=i+1
 584 
 585     		if (column_header == 1):
 586        			col_names=' ABCDEFGHIJKLMNOPQRSTUVWXYZ'
 587        			x='||'
 588        			if(row_header == 1):
 589           			start=0
 590        			else:
 591           			start=1
 592        			for name in col_names[start:c]:
 593            			x = x + "%(format)s %(value)s %(tab)s" % {
 594 	                    		"format": '<:#CCCCCC>',
 595 	                    		"value": "'''"+name+"'''",
 596 		            		"tab":'||'}
 597 
 598        			matrix.insert(0,x)
 599 
 600 		wikiizer = wiki.Parser(unicode(string.join(matrix,"\n"),'latin-1'),self.request) # parser for wiki tabular
 601 		wikiizer.format(formatter)

Attached Files

To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.
  • [get | view] (2006-02-16 14:59:57, 14.6 KB) [[attachment:Calendar-20060216.py]]
  • [get | view] (2007-05-28 09:42:03, 2.7 KB) [[attachment:ExcelPastedTable.py]]
  • [get | view] (2005-04-12 19:22:18, 6.0 KB) [[attachment:Gantt-1.3.3-2.py]]
  • [get | view] (2007-03-24 02:05:26, 3.9 KB) [[attachment:Literate_parser-0.7_Moin-1.3.tgz]]
  • [get | view] (2007-03-24 02:08:08, 4.8 KB) [[attachment:Literate_parser-0.7_Moin-1.3.zip]]
  • [get | view] (2005-03-11 13:50:49, 12.3 KB) [[attachment:MySQL.py]]
  • [get | view] (2005-09-11 08:09:44, 1.6 KB) [[attachment:SortText-1.3.5-1.py]]
  • [get | view] (2005-11-21 08:40:10, 2.8 KB) [[attachment:Sorter-1.3.py]]
  • [get | view] (2005-06-02 13:02:06, 1.2 KB) [[attachment:colorer.py]]
  • [get | view] (2006-01-04 16:10:31, 0.6 KB) [[attachment:gettext.py]]
  • [get | view] (2004-10-19 13:05:05, 0.7 KB) [[attachment:html-parser-1.2.py]]
  • [get | view] (2005-02-17 10:46:56, 0.6 KB) [[attachment:html.py]]
  • [get | view] (2005-12-06 21:09:48, 1.3 KB) [[attachment:matlab.py]]
  • [get | view] (2005-01-20 07:42:34, 0.4 KB) [[attachment:nocamelcase.py]]
  • [get | view] (2005-11-28 16:55:23, 2.3 KB) [[attachment:php-1.3.4-1]]
  • [get | view] (2005-12-18 22:36:37, 15.0 KB) [[attachment:sctable-1.3.5-4.py]]
  • [get | view] (2004-12-31 04:41:23, 1.6 KB) [[attachment:textil.py]]
 All files | Selected Files: delete move to page copy to page

You are not allowed to attach a file to this page.