Novo site do Colégio!

Publiquei hoje o novo site do Colégio Salesiano Itajaí, um site totalmente administrável (até o título pode ser mudado via formulários) que usa a linguagem PHP e o banco de dados MySql. O site também segue os padrões web, mesmo com formulários rich text que às vezes deixam o site pouco semântico (é que os monitores do Colégio devem poder atualizar) e está disponível em: salesianoitajai.g12.br.

A notícia de estréia da página está disponível aqui e conta com um lindo screenshot do meu Fluxbox com Mrxvt e o Vim com três splits. Embora o site já esteja publicado, ainda tenho que arrumar alguns detalhes como por exemplo o Sitemap que eu cito nesta notícia.

Uma coisa bem legal que eu coloquei no site foi um JavaScript, usando a função addGlobalStyle do Dive into Greasemonkey, que nos Mozillas (ereg(“Gecko/”, $\SERVER[“HTTP_USER_AGENT”]))_ faz o site ficar maior se o usuário usar uma resolução igual ou maior a 1024×768. Dá pra fazer pra todos os navegadores, mas eu sou meio newbie em JavaScript e por isso só usei esse código do Greasemonkey, que só funciona nos Mozillas mesmo… Mas o resultado ficou ótimo! :)

Ontem o Sr. Paulo Matias me ensinou como trabalhar com operadores bit-a-bit no C. Achei muito massa! Agora comecei até a economizar fazendo:

numero = outronumero << 1;

… ao invés de simplesmente:

numero = outronumero * 2;

… para meus programas serem mais rápidos! Hehehe :D Fiz uma função que converte de decimal para binário, e depois vou postar aqui.

Observação: Tenho que descobrir porque os emoticons não funcionam quando tem código... Quer dizer, descobrir eu já descobri, mas preciso ver como eu faço pra resolver de forma boa essas expressões regulares...

Editado!

Corrigi o problema… Confira abaixo o novo trecho de ERs da função de emoticons, onde $o é o vetor de origem (com as carinhas originais), $d é o vetor de desgino (com o <img src…) e replace() é uma função que eu criei para substituir os emoticons:

<?php
if (!ereg("<code", $texto)) {
	$texto=replace($o, $d, $texto);
} else {
	preg_match_all("/</code>(.+)<code/sU", $texto, $mat1);
	for ($i=0; $i<sizeof($mat1[1]); $i++) {
		$texto=str_replace($mat1[1][$i], replace($o, $d, $mat1[1][$i]), $texto);
	}
	preg_match("/^(.+)<code/sU", $texto, $mat2);
	$texto=str_replace($mat2[1], replace($o, $d, $mat2[1]), $texto);
	$rev=strrev($texto); //Tenho medo do PHP5.0.5
	preg_match("/^(.+)>edoc/</sU", $rev, $mat3); //Nossa, que código louco!
	$rev=strrev($mat3[1]); //Repito... Tenho medo do PHP5.0.5
	$texto=str_replace($rev, replace($o, $d, $rev), $texto);
}
?>

Ficou feio, né? Não consegui pensar em outra coisa mais fácil que usar o strrev() ali… :blink:

De volta à resolução de problemas

Resultado do Superprime Rib

Hoje, depois de umas férias de dois meses, resolvi um problema lógico do USACO Training Gateway: o Superprime Rib é um problema bem simples em que precisa-se determinar os primos de N dígitos (com N máximo = 8 ) que, tirando o último dígito, continuam sendo primos. A solução é trivial, uma função recursiva bastante simples que se auto-explica no meu código:

//Superprime Rib - USACO Training Gateway - 2005

/*
ID: contato1
PROG: sprime
LANG: C
*/

#include <stdio.h>
#define NMAX 9
#define INFINITO 100000

int primos[NMAX][INFINITO], cont[NMAX];

int eh_primo(long int num) {
	int i;

	if (num==1||(!(num%2)&&num!=2)) {
		return 0;
	}

	for (i=3; i*i<=num; i+=2) {
		if (!(num%i)) {
			return 0;
		}
	}

	return 1;
}

void funcao(int n) {
	int i, j, num;

	cont[n]=0;

	if (n>1) {
		funcao(n-1);

		for (i=0; i<cont[n-1]; i++) {
			for (j=1; j<=9; j+=2) {
				num=primos[n-1][i]*10+j;
				if (eh_primo(num)) {
					primos[n][cont[n]++]=num;
				}
			}
		}
	} else {
		primos[1][0]=2;
		primos[1][1]=3;
		primos[1][2]=5;
		primos[1][3]=7;
		cont[1]=4;
	}
}

int main() {
	int n, i;

	FILE *in=fopen("sprime.in", "r");
	FILE *out=fopen("sprime.out", "w");
	fscanf(in, "%d", &n);
	fclose(in);

	funcao(n);

	for (i=0; i<cont[n]; i++) {
		fprintf(out, "%d\n", primos[n][i]);
	}
	fclose(out);

	return 0;
}

O problema passou de segunda porque na primeira, por falta de hábito, eu tinha colocado scanf e printf ao invés de usar o sistema da USACO onde deve-se usar arquivos de entrada e saída.

Agora para eu ir para a seção 2 do USACO Training Gateway falta só o programa Checker Challenge, que parece ser complicado.

Instalei os pacotes do Slackware 10.2, que saiu essa semana, no laptop. Não tem nenhuma grande mudança, mas é sempre bom estar com os programas atualizados…

O Paulo Matias (Thotypous) me convidou para fazer parte da equipe de desenvolvimento da distro Guaranix, consertando alguns bugs do XDirectFB (que eu citei aqui). Acho que irei pegar um trabalho com a Meetweb também (o Hugo Dias, para quem eu fiz o serviço da Coalizão Antituberculose me convidou) e estou acabando o site do Colégio Salesiano, que é totalmente administrável em PHP e usa um banco de dados MySql. Ele deve sair semana que vem…

Dia 24 é a segunda fase da Olimpíada Regional de Matemática. Essa semana fiz a folhinha de treinamento e dos seis problemas, consegui fazer cinco (na verdade, alguns problemas - ou todos - eram repetidos do ano anterior e por isso fica mais fácil, porque eu já lembrava o caminho).

Agenda C+MySql e Anúncio da OBI2005

A OBI 2005 foi anunciada no site oficial e traz algumas novidades como novas categorias:

Anúncio no Site Oficial

Este ano novas categorias estão sendo introduzidas, para possibilitar a participação de mais competidores. As modalidades da OBI2005 são:

  • Modalidade Iniciação:

    • Nível 1, para alunos até a sexta série do Ensino Fundamental e
    • Nível 2, para alunos até a oitava série do Ensino Fundamental.
  • Modalidade Programação:

    • Nível 1, para alunos até o primeiro ano do ensino médio e
    • Nível 2, para alunos até o terceiro ano do ensino médio (ou que tenham cursado o ensino médio até dezembro de 2004).

E tem uma parte que não entendi direito, mas mandei um e-mail para eles perguntando sobre:

Em 2005 a IOI será realizada na Polônia, de 18 a 25 de agosto. Quatro competidores da Modalidade Programação, Nível 2, representarão o Brasil. Você pode ser um deles!

Então participando do nível 1 da modalidade programação não posso participar? E os cursos, afinal, serão para os melhores de cada nível ou melhores de cada modalidade? Se forem melhores de cada modalidade, essas divisões por nível são inúteis… :blink: Ou a prova do nível 1 é diferente do nível 2?

Criei minha agenda C+MySql com conhecimentos recém-adquiridos. Ela só faz três funções básicas: cadastrar, ver e deletar. Ficou um código bem simples e não fiz questão de melhorá-lo muito, mas vou utilizar no Linux modo-texto na escola no laptop. Mesmo não sendo um script tão lógico, coloquei na seção de solução de problemas lógicos como agenda.c.

Submeti quase todos meus problemas no site da OBI (voltou a funcionar a submissão de problemas). Contei todos e fiz a média: 60% dos problemas corretos. Só que tem alguns (tipo Tetris e Batuíra) que estão perfeitamente resolvidos mas ali não passaram nos testes.

Consegui usar JavaScript no Orkut também:

#"onmouseover="while(1)window.close

Isso travou meu Firefox no Linux… :lol:

Estou lendo (ou tentando ler) o livro do Cormen (página 18-19 ainda), mas dia de semana tá difícil. Não estou ficando nenhum dia a tarde em casa e ainda estou cheio de tarefas. Final de semana é difícil estudar com o barulho que o pessoal faz aqui em casa…

Estatísticas de Visita com Shortstat

Comecei a usar o Shortstat para acompanhar as estatísticas do site. Porém, tive alguns problemas com ele (não exatamente problemas, mas coisas que eu acho melhor mudar). Exemplos:

  • As estatísticas de browser e sistema são contadas por hit… Eu acho muito mais sensato contar por visita (única), pois algumas pessoas contam vários hits (exemplo: eu) e daí as minhas estatísticas estavam dizendo que 80% dos visitantes usavam Linux!
  • Os webcrawlers e alguns browsers são registrados como sistema operacional desconhecido

Já que o sistema é feito em PHP, achei legal consertar estes problemas e até traduzir e colocar uma bandeira do lado dos países. emoticon Vou postar aqui um passo-a-passo de instalação e esas configuração do Shortstat para quem precisar. Achei ele um ótimo sistema de estatísticas (código super simples e bem direto) e tem tudo que eu preciso. :)

Introdução

O Shortstat é um programa de estatísticas da ShaunInman escrito em PHP que usa um banco de dados MySql para incluir os registros. O funcionamento é bastante simples. Em cada página, eu uso um include para um arquivo que conta visita e existe um arquivo que conta as estatísticas. Estou partindo do princípio que você já tem PHP e MySql configurados num servidor web.

Download

O download do programa .zip pode ser feito aqui:

http://www.shauninman.com/downloads/shortstat_v036b.zip

No Linux, use o comando unzip shortstat_v036b.zip para descompactar.

No Windows, use um programa como WinZip ou WinRar (ou o descompactador do Windows XP).

Arquivos Descompactados

  • configuration.php – Configuração do banco de dados
  • functions.php – Funções do programa (toda a parte de PHP)
  • inc.stats.php – Arquivo que deve ser incluído em cada página do site pra contar visita
  • index.php – Página onde se vê as estatísticas
  • styles.css – Estilos (css) da página index.php

E ainda tem os arquivos de instalação (que poderão ser deletados logo que acabar a instalação).

IP dos Países

O Shortstat vem com um arquivo de 2mb (_ip-to-country.txt) que tem um banco de dados com ip de vários países e outro php (_ip-to-country.php) que serve para instalar o suporte ao “ip-to-country”. Depois de instalar o Shortstat normal, nós vamos instalar também para saber de onde são os visitanets do site.

Instalação

Para instalar o Shortstat, edite o arquivo configuration.php colocando nas variáveis:

<?php
$SI_db['server']="servidor_do_mysql";
$SI_db['username']="username_do_mysql";
$SI_db['password']="senha_do_mysql";
$SI_db['database']="nome_do_banco_de_dados";
$tz_offset=seu_fuso_horario;
$shortstat=true;
?>

E rode o script de instalação (_install.php) no seu browser. Ele irá criar as tabelas no seu banco de dados. Daí basta acrescentar:

<?php @include_once("diretorio_do_shortstat/inc.stats.php"); ?>

… no início de cada arquivo que você quer que sejam contadas as estatísticas.

Antes de instalar o ip-to-country, eu criei uma coluna chamada codigopais no banco de dados MySql e modifiquei o arquivo functions.php criando uma função chamada verCodigoPais:

<?php
function verCodigoPais($ip) {
       if (!SI_isIPtoCountryInstalled()) return '';
       global $SI_tables;
       $ip = sprintf("%u",ip2long($ip));

       $query="SELECT country_code2 AS codigo FROM $SI_tables[countries] WHERE ip_from <= $ip AND ip_to >= $ip";
       if ($result=mysql_query($query)) {
              if ($r = mysql_fetch_array($result)) {
                     return $r['codigo'];
              }
       }
}
?>

No arquivo inc.stats.php, depois de atribuir um valor para a variável $ip, coloquei:

<?php
$cd     = verCodigoPais($ip);
?>

… e depois de colocar valor em todas as variáveis alterei a $query para:

<?php
$query = "INSERT INTO $SI_tables[stats] (remote_ip, country, codigopais,
domain, referer, resource, user_agent, platform, browser, version, dt)
VALUES ('$ip', '$cntry', '$cd', '$domain', '$ref', '$res', '$ua', '$br[platform]',
'$br[browser]', '$br[version]', $dt)";
?>

(a única mudança foi a adição da variável cd – que contém o código do país – no campo codigopais do banco de dados)

Agora basta imprimir a bandeira do país… Para isso, no arquivo functions.php alterei a função SI_getCountries:

<?php
function SI_getCountries() {
       global $SI_tables,$_SERVER;

       $query = "SELECT country, codigopais, COUNT(distinct(remote_ip)) AS 'total'
                       FROM $SI_tables[stats]
                       WHERE country!=''
                       GROUP BY country
                       ORDER BY total DESC";

       if ($result = mysql_query($query)) {
              $ul  = "<table cellpadding="0" cellspacing="0" border="0">n";
              $ul .= "t

<tr>
  <th>
    Country
  </th>

  <th class="last">
    Visits
  </th>
</tr>n";
              $i=0;
              while ($r = mysql_fetch_array($result)) {
                     if ($i < 36) {
                            $url = parse_url($r[referer]);
                            $ul .= "t

<tr>
  <td>
    <img src="http://ip-to-country.webhosting.info/flag/?type=3&cc2=$r[codigopais]" alt="$r[codigopais]" /> $r[country]
  </td>

  <td class="last">
    $r[total]
  </td>
</tr>n";
                            $i++;
                            }
                     }
              $ul .= "</table>";
              }
       return $ul;
}
?>

(note que a imagem é buscada direto do servidor do ip-to-country)

Então, agora é só instalar o ip-to-country, mas o arquivo de instalação só está servindo para colocar o nome do país no banco de dados (não o código). Basta modificar o arquivo _ip-to-country.php, alterando:

<?php
echo "<p>Mapping existing IPs to countries.</p>";
// Match existing ips to countries
$query = "SELECT id,remote_ip FROM $SI_tables[stats] WHERE country=''";
if ($result = mysql_query($query)) {
   while ($r = mysql_fetch_array($result)) {
      $country = SI_determineCountry($r[remote_ip]);
      $query = "UPDATE $SI_tables[stats] SET country='$country' WHERE id=$r[id]";
      mysql_query($query);
   }
}
?>

… para…

<?php
echo "<p>Mapping existing IPs to countries.</p>";
// Match existing ips to countries
$query = "SELECT id,remote_ip FROM $SI_tables[stats] WHERE country=''";
if ($result = mysql_query($query)) {
   while ($r = mysql_fetch_array($result)) {
      $country = SI_determineCountry($r[remote_ip]);
      $cd = verCodigoPais($r[remote_ip]);
      $query = "UPDATE $SI_tables[stats] SET country='$country' AND codigopais='$cd' WHERE id=$r[id]";
      mysql_query($query);
   }
}
?>

Daí é só rodar o arquivo _ip-to-country.php e o ip-to-country estará funcionando junto com o shortstat com bandeira do lado do país! :)

Estatísticas por visitas, não por hits

Eu alterei a função SI_getPlatforms por:

<?php
function SI_getPlatforms() {
       global $SI_tables;
       $th = SI_getUniqueHits();
       $query = "SELECT platform, COUNT(distinct(remote_ip)) AS 'total'
                       FROM $SI_tables[stats]
                       GROUP BY platform
                       ORDER BY total DESC";
       if ($result = mysql_query($query)) {
              $ul  = "<table cellpadding="0" cellspacing="0" border="0">n";
              $ul .= "t

<tr>
  <th>
    Platform
  </th>

  <th class="last">
    %
  </th>
</tr>n";
              while ($r = mysql_fetch_array($result)) {
                     $ul .= "t

<tr>
  <td>
    $r[platform]
  </td>

  <td class="last">
    ".number_format(($r[total]/$th)*100)."%
  </td>
</tr>n";
                     }
              $ul .= "</table>";
              }
       return $ul;
}
?>

A mudança foi o count usar distinct(remoteip) e o $th ter o valor dos hits únicos (daí a porcentagem é contada a partir deles). A mudança na função **SIgetBrowsers** é semelhante:

<?php
function SI_getBrowsers() {
       global $SI_tables;
       $th = SI_getUniqueHits();
       $query = "SELECT browser, version, COUNT(distinct(remote_ip)) AS 'total'
                       FROM $SI_tables[stats]
                       WHERE browser != 'Indeterminable'
                       GROUP BY browser, version
                       ORDER BY total DESC";
       if ($result = mysql_query($query)) {
              $ul  = "<table cellpadding="0" cellspacing="0" border="0">n";
              $ul .= "t

<tr>
  <th>
    Browser
  </th>

  <th>
    Version
  </th>

  <th class="last">
    %
  </th>
</tr>n";
              while ($r = mysql_fetch_array($result)) {
                     $p = number_format(($r[total]/$th)*100);
                     // $p = ($p==0)?"<1":$p;
                     if ($p>=1) {
                            $ul .= "t

<tr>
  <td>
    $r[browser]
  </td>

  <td>
    $r[version]
  </td>

  <td class="last">
    $p%
  </td>
</tr>n";
                            }
                     }
              $ul .= "</table>";
              }
       return $ul;
}
?>

Conclusão

Assim temos um Shortstat configurado para as minhas necessidades. Eu gosto assim, mas por ser um sistema de código bastante simples em PHP você pode configurar mais o que quiser. Eu traduzi (é só modificar as coisas no index.php) também (não tem uma grande utilidade, mas não custa…)

Espero que tenham gostado do “artigo” e qualquer dúvida ou crítica, postem um comentário ou enviem um e-mail.

© 2005–2020 Tiago Madeira